#!/usr/bin/python2 # -*- coding: utf-8 -*- """Run a reverse-digit-span auditory memory test, like Wechsler’s. Very hacky but probably okay. See analrev.py for analysis. A couple of TODOs: - it’s getting increasingly harder to motivate myself to do this, although I’m getting better at it, because I always fail almost half the trials. As an example, my session last night was 10 trials in 3'5", of which only 6 were successful, and half of those were at the 7-digit-and-less level where I’m usually successful (90% of the time at 6 digits, 75% of the time at 7 digits, over the last 104 trials covering 3 days). FreeRice requires five consecutive successful trials to go up a difficulty level, and even the Wechsler test provides two trials at each level, starting at the very easy level of 2, and terminates as soon as both trials on a level are unsuccessful. This means that a typical person who fails at 6 digits and above will have 8 successful trials at spans of 2, 3, 4, and 5 before their 2 unsuccessful trials at level 6. So a theoretically “less efficient” approach might actually get better results — even if it takes 6 minutes instead of 3 minutes, I might not dread those 6 minutes. I’ve switched to the Wechsler protocol (except with no ceiling) and initial tests seem promising: a test took 5'4" instead of the usual 3' or so, and I missed on 6 out of 20 trials, thus succeeding 70% of the time instead of 60%. Also, the successes were mostly toward the beginning. It does seem to have provided me with a significant performance boost, which means that comparability with previous results is imperfect, but I should be able to calibrate that. - Along the same lines, some kind of fireworks or something would be nice after a successful trial. """ import os import random import time import sys import pygame class open_log: def __init__(self, filename): self.fo = open(os.path.join(os.environ.get('HOME', '/'), filename), 'a') self.log("session start") def log(self, message, *args): try: self.fo.write("%d " % time.time()) self.fo.write(message % args) finally: self.fo.write("\n") self.fo.flush() class start_stopwatch: def __init__(self): self.start = time.time() def time(self): return time.time() - self.start class half_wins: "My first difficulty strategy, tending toward half wins." def __init__(self, start_level=6, trials=None): self.digits = start_level if trials is None: trials = random.randrange(8, 10) + random.randrange(4) self.trials = trials def win(self): self.digits += 1 self.trials -= 1 def lost(self): if self.digits > 1: self.digits -= 1 self.trials -= 1 # maybe add sum(random.randrange(2) for ii in range(13))//2 - 3? # that way we can sample a bit more around the person's # range and avoid them predicting the number of digits. @property def done(self): return self.trials <= 0 class wechsler: "The standard Wechsler strategy, but without the ceiling at 9." def __init__(self, start_level=2, trials_per_level=2): self.digits = self.start_level = start_level self.trials_per_level = trials_per_level self.trials = trials_per_level # left at this level self.losses = 0 # at this level self.done = False def __str__(self): return 'wechsler(start_level={}, trials_per_level={})'.format( self.start_level, self.trials_per_level) def won(self): self.next() def lost(self): self.losses += 1 self.next() def next(self): self.trials -= 1 if self.trials == 0: if self.losses == self.trials_per_level: self.done = True else: self.digits += 1 self.trials = self.trials_per_level self.losses = 0 def main(argv): pygame.init() log = open_log('reversedigitspan.log') screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN) mydir = os.path.dirname(__file__) if '--lang' in argv: lang = argv[argv.index('--lang')+1] + '-' else: lang = '' sounds = dict((i, pygame.mixer.Sound(os.path.join(mydir, '%sdigit%d.ogg' % (lang, i)))) for i in range(10)) font = pygame.font.Font(None, 96) strategy = wechsler() log.log("strategy is %s", strategy) state = 'naming' name = '' while not strategy.done: ev = pygame.event.poll() if ev.type == pygame.QUIT: break if ev.type == pygame.KEYDOWN: # XXX no autorepeat? if state not in ('accepting', 'naming'): continue if state == 'accepting': if ev.unicode == '\033': # Esc break if '0' <= ev.unicode <= '9': entered += ev.unicode # maybe also play the digit out loud? elif ev.unicode == '\x08': # backspace entered = entered[:-1] elif ev.unicode == '\r': # cr if entered == ''.join(reversed([str(i) for i in digits])): log.log("correct reverse digit span of %d by %r in %.3f\"", len(digits), name, stopwatch.time()) state = 'correct' strategy.won() else: log.log("incorrect reverse digit span of %d by %r in %.3f\"", len(digits), name, stopwatch.time()) state = 'incorrect' strategy.lost() del entered, stopwatch last_digit = time.time() - 1 digits = [random.randrange(10) for i in range(strategy.digits)] digit_position = 0 continue if state == 'naming': if ev.unicode == '\033': # Esc break if ev.unicode == '\x08': # backspace name = name[:-1] elif ev.unicode == '\r': state = 'speaking' last_digit = time.time() - 1 digits = [random.randrange(10) for i in range(strategy.digits)] digit_position = 0 else: name += ev.unicode continue pygame.time.delay(10) now = time.time() if state in ('speaking', 'correct', 'incorrect') and now - last_digit > 1: if digit_position < len(digits): sounds[digits[digit_position]].play() last_digit += 1.0 digit_position += 1 else: state = 'accepting' entered = '' stopwatch = start_stopwatch() screen.fill((0, 0, 0) if state in ('naming', 'speaking') else (128, 128, 255) if state == 'accepting' else (255, 128, 128) if state == 'correct' else (63, 63, 63) if state == 'incorrect' else (0, 255, 0) # error ) if state == 'accepting': if entered: screen.blit(font.render(entered, 1, (0, 0, 0)), (100, 100)) else: screen.blit(font.render("Type digits backwards and press Enter.", 1, (0, 0, 0)), (200, 200)) screen.blit(font.render(u"Tecleá las cifras al revés y apretá Enter.".encode('latin-1'), 1, (0, 0, 0)), (200, 300)) if state in ('correct', 'incorrect'): text = font.render(state, 1, (0, 0, 0)) screen.blit(text, (200, 200)) if state == 'speaking': screen.blit(font.render("Type digits backwards and press Enter.", 1, (255, 255, 255)), (200, 200)) screen.blit(font.render(u"Tecleá las cifras al revés y apretá Enter.".encode('latin-1'), 1, (255, 255, 255)), (200, 300)) if state == 'naming': screen.blit(font.render(name, 1, (128, 128, 255)), (100, 100)) if not name: screen.blit(font.render("Type your name and press Enter.", 1, (255, 255, 255)), (200, 200)) screen.blit(font.render(u"Entrá tu nombre y apretá Enter.".encode('latin-1'), 1, (255, 255, 255)), (200, 300)) pygame.display.flip() if __name__ == '__main__': main(sys.argv)