Skip to content

Commit 1465e6c

Browse files
committed
Add an option for 2-answer scheme.
1 parent a1b4a8b commit 1465e6c

File tree

2 files changed

+102
-37
lines changed

2 files changed

+102
-37
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ An application for learning multiplication table.
77
### Basic Operation
88

99
The application selects a question from a 10x10 multiplication table and displays it in the middle of the screen.
10-
It also displays four answers to choose from: below, above and on both sides of the question.
10+
It also displays possible answers to choose from, next to the question.
1111
Only one of the answers is correct.
1212

1313
The user needs to select the correct answer by pressing a key corresponding to the position of the answer.
@@ -52,6 +52,15 @@ They are displayed in the lower corners of the main window.
5252
- Pass the `--show-scores` option to show scores.
5353
- Use the`--score-font` option to select the font to use for displaying scores.
5454

55+
### Choosing from two or four possible answers
56+
57+
By default the application displays four possible answers.
58+
They are shown below, above and on both sides of the question.
59+
- Pass the `--answer-scheme=EW` option to show only two possible answers.
60+
- Pass the `--answer-scheme=NESW` option to show four possible answers.
61+
62+
In either mode, only one of the displayed answers is the correct one.
63+
5564
### Limiting the Number of Questions
5665

5766
By default the application will keep asking questions until it is closed.

tabliczka.py

Lines changed: 92 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@
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+
443499
class Problem:
444-
500+
445501
def __init__(self, a, b, answer_count):
446502
self._a = a
447503
self._b = b

0 commit comments

Comments
 (0)