3838_FREQ_QUICK = 1
3939_ANSWER_SEC_QUICK = 2
4040_DEFAULT_SCORE_FONT = 'monospace'
41+ _DEFAULT_ANSWER_SCHEME = 'NESW'
4142
43+ _KEYS_ARROWS = (pygame .K_UP , pygame .K_RIGHT , pygame .K_DOWN , pygame .K_LEFT )
44+ # The order of the following matches the order of the above.
4245_KEYS_MINECRAFT_LOWER = 'wdsa'
4346_KEYS_MINECRAFT_UPPER = 'WDSA'
44- _KEYS_ARROWS = (pygame .K_UP , pygame .K_RIGHT , pygame .K_DOWN , pygame .K_LEFT )
4547
4648_home = os .path .expanduser ('~' )
4749_xdg_state_home = os .environ .get ('XDG_STATE_HOME' ) or os .path .join (_home , '.local' , 'state' )
@@ -67,6 +69,7 @@ def get_argument_parser():
6769 parser .add_argument ('--show-feedback' , action = argparse .BooleanOptionalAction , help = 'Show feedback on wrong answers.' )
6870 parser .add_argument ('--show-scores' , action = argparse .BooleanOptionalAction , help = 'Show scores in main window.' )
6971 parser .add_argument ('--score-font' , help = 'Font to use for displaying scores (defaults to %s).' % _DEFAULT_SCORE_FONT )
72+ parser .add_argument ('--answer-scheme' , choices = [_DEFAULT_ANSWER_SCHEME , 'EW' ], default = None , help = 'Where to show possible answers (letters stand for geographic directions relative to displayed question).' )
7073
7174 return parser
7275
@@ -121,7 +124,7 @@ class Settings:
121124
122125 def __init__ (self , fs , parsed_args ):
123126 self ._s = dict ((k , None ) for k in [
124- 'limit' , 'show_scores' , 'show_feedback' , 'score_font' ])
127+ 'limit' , 'show_scores' , 'show_feedback' , 'score_font' , 'answer_scheme' ])
125128 self ._load_settings (fs )
126129 self ._merge_settings (parsed_args )
127130 self ._save_settings (fs )
@@ -142,6 +145,10 @@ def show_feedback(self):
142145 def score_font (self ):
143146 return self ._s ['score_font' ] or _DEFAULT_SCORE_FONT
144147
148+ @property
149+ def answer_scheme (self ):
150+ return self ._s ['answer_scheme' ] or _DEFAULT_ANSWER_SCHEME
151+
145152 def _load_settings (self , fs ):
146153 loaded = fs .read ()
147154 if not loaded :
@@ -299,6 +306,7 @@ def __init__(self, settings):
299306 self ._should_show_scores = settings .show_scores
300307 self ._should_show_feedback = settings .show_feedback
301308 self ._score_font_name = settings .score_font
309+ self ._answer_scheme = settings .answer_scheme
302310
303311 def __enter__ (self ):
304312 logging .debug ('Initializing pygame.' )
@@ -327,10 +335,10 @@ def _tick(self):
327335 self ._clock .tick (30 ) # low framerate is fine for this app
328336
329337 def answer_count (self ):
330- return 4
338+ return len ( self . _answer_scheme )
331339
332340 def solve_problem (self , problem , state ):
333- answers = self ._display_problem (problem , state )
341+ answer_map = self ._display_problem (problem , state )
334342
335343 asked_time = time .time ()
336344
@@ -342,16 +350,12 @@ def solve_problem(self, problem, state):
342350 logging .debug ('Initiating shutdown.' )
343351 raise QuitException ()
344352 if event .type == pygame .KEYDOWN :
345- if event .unicode and event .unicode in _KEYS_MINECRAFT_LOWER :
346- answer_index = _KEYS_MINECRAFT_LOWER .index (event .unicode )
347- elif event .unicode and event .unicode in _KEYS_MINECRAFT_UPPER :
348- answer_index = _KEYS_MINECRAFT_UPPER .index (event .unicode )
349- elif event .key in _KEYS_ARROWS :
350- answer_index = _KEYS_ARROWS .index (event .key )
353+ logging .debug (answer_map )
354+ if answer_map .has_answer_for (event ):
355+ problem .answered (answer_map .answer_for (event ), asked_time )
356+ return
351357 else :
352358 continue
353- problem .answered (answers [answer_index ], asked_time )
354- return
355359
356360 def provide_feedback (self , problem , state ):
357361 if not self ._should_show_feedback :
@@ -374,12 +378,11 @@ def _display_problem(self, problem, state, reveal_solution=False):
374378 self ._show_correct_score (state )
375379 self ._show_error_score (state )
376380 self ._show_question (problem )
377- answers = problem .answers ()
378- self ._show_answers (problem , answers , reveal_solution = reveal_solution )
381+ answer_map = self ._show_answers (problem , problem .answers (), reveal_solution = reveal_solution )
379382 logging .debug ('Updating display.' )
380383 pygame .display .flip ()
381384 logging .debug ('Problem displayed.' )
382- return answers
385+ return answer_map
383386
384387 def _show_correct_score (self , state ):
385388 screen_bottom_left = self ._screen .get_rect ().bottomleft
@@ -412,26 +415,42 @@ def _show_question(self, problem):
412415
413416 def _show_answers (self , problem , answers , reveal_solution = False ):
414417 screen_center = self ._screen .get_rect ().center
415-
416- answer_up = self ._font .render (answers [0 ], 1 , self ._text_color )
417- answer_up_rect = answer_up .get_rect (center = (screen_center [0 ], int (1.5 * self ._digit_size [1 ])))
418- pygame .draw .rect (self ._screen , self ._answer_color (problem , answers [0 ], reveal_solution ), answer_up_rect )
419- self ._screen .blit (answer_up , answer_up_rect )
420-
421- answer_right = self ._font .render (answers [1 ], 1 , self ._text_color )
422- answer_right_rect = answer_right .get_rect (center = (int (21 * self ._digit_size [0 ]), screen_center [1 ]))
423- pygame .draw .rect (self ._screen , self ._answer_color (problem , answers [1 ], reveal_solution ), answer_right_rect )
424- self ._screen .blit (answer_right , answer_right_rect )
425-
426- answer_down = self ._font .render (answers [2 ], 1 , self ._text_color )
427- answer_down_rect = answer_down .get_rect (center = (screen_center [0 ], int (5.5 * self ._digit_size [1 ])))
428- pygame .draw .rect (self ._screen , self ._answer_color (problem , answers [2 ], reveal_solution ), answer_down_rect )
429- self ._screen .blit (answer_down , answer_down_rect )
430-
431- answer_left = self ._font .render (answers [3 ], 1 , self ._text_color )
432- answer_left_rect = answer_left .get_rect (center = (int (3 * self ._digit_size [0 ]), screen_center [1 ]))
433- pygame .draw .rect (self ._screen , self ._answer_color (problem , answers [3 ], reveal_solution ), answer_left_rect )
434- self ._screen .blit (answer_left , answer_left_rect )
418+ answers = list (answers ) # copy before mutating the list
419+ answer_map = AnswerMap ()
420+
421+ if 'N' in self ._answer_scheme :
422+ answer_up = answers .pop (0 )
423+ answer_up_surface = self ._font .render (answer_up , 1 , self ._text_color )
424+ answer_up_rect = answer_up_surface .get_rect (center = (screen_center [0 ], int (1.5 * self ._digit_size [1 ])))
425+ pygame .draw .rect (self ._screen , self ._answer_color (problem , answer_up , reveal_solution ), answer_up_rect )
426+ self ._screen .blit (answer_up_surface , answer_up_rect )
427+ answer_map .answer_up (answer_up )
428+
429+ if 'E' in self ._answer_scheme :
430+ answer_right = answers .pop (0 )
431+ answer_right_surface = self ._font .render (answer_right , 1 , self ._text_color )
432+ answer_right_rect = answer_right_surface .get_rect (center = (int (21 * self ._digit_size [0 ]), screen_center [1 ]))
433+ pygame .draw .rect (self ._screen , self ._answer_color (problem , answer_right , reveal_solution ), answer_right_rect )
434+ self ._screen .blit (answer_right_surface , answer_right_rect )
435+ answer_map .answer_right (answer_right )
436+
437+ if 'S' in self ._answer_scheme :
438+ answer_down = answers .pop (0 )
439+ answer_down_surface = self ._font .render (answer_down , 1 , self ._text_color )
440+ answer_down_rect = answer_down_surface .get_rect (center = (screen_center [0 ], int (5.5 * self ._digit_size [1 ])))
441+ pygame .draw .rect (self ._screen , self ._answer_color (problem , answer_down , reveal_solution ), answer_down_rect )
442+ self ._screen .blit (answer_down_surface , answer_down_rect )
443+ answer_map .answer_down (answer_down )
444+
445+ if 'W' in self ._answer_scheme :
446+ answer_left = answers .pop (0 )
447+ answer_left_surface = self ._font .render (answer_left , 1 , self ._text_color )
448+ answer_left_rect = answer_left_surface .get_rect (center = (int (3 * self ._digit_size [0 ]), screen_center [1 ]))
449+ pygame .draw .rect (self ._screen , self ._answer_color (problem , answer_left , reveal_solution ), answer_left_rect )
450+ self ._screen .blit (answer_left_surface , answer_left_rect )
451+ answer_map .answer_left (answer_left )
452+
453+ return answer_map
435454
436455
437456 def _answer_color (self , problem , answer , reveal_solution = False ):
@@ -440,8 +459,45 @@ def _answer_color(self, problem, answer, reveal_solution=False):
440459 return self ._answer_correct_color if problem .correct_answer () == answer else self ._answer_error_color
441460
442461
462+ class AnswerMap :
463+
464+ def __init__ (self ):
465+ self ._answers = dict (
466+ up = None ,
467+ right = None ,
468+ down = None ,
469+ left = None )
470+
471+ def answer_up (self , answer ):
472+ self ._answers ['up' ] = answer
473+
474+ def answer_right (self , answer ):
475+ self ._answers ['right' ] = answer
476+
477+ def answer_down (self , answer ):
478+ self ._answers ['down' ] = answer
479+
480+ def answer_left (self , answer ):
481+ self ._answers ['left' ] = answer
482+
483+ def has_answer_for (self , event ):
484+ return self .answer_for (event ) != None
485+
486+ def answer_for (self , event ):
487+ if event .unicode and event .unicode in _KEYS_MINECRAFT_LOWER :
488+ answer_index = _KEYS_MINECRAFT_LOWER .index (event .unicode )
489+ elif event .unicode and event .unicode in _KEYS_MINECRAFT_UPPER :
490+ answer_index = _KEYS_MINECRAFT_UPPER .index (event .unicode )
491+ elif event .key and event .key in _KEYS_ARROWS :
492+ answer_index = _KEYS_ARROWS .index (event .key )
493+ else :
494+ return None
495+ direction = ['up' , 'right' , 'down' , 'left' ][answer_index ]
496+ return self ._answers [direction ]
497+
498+
443499class Problem :
444-
500+
445501 def __init__ (self , a , b , answer_count ):
446502 self ._a = a
447503 self ._b = b
0 commit comments