Created
March 18, 2022 10:49
-
-
Save initzx/cb6077926a148d58f1f2141ec09f2c0a to your computer and use it in GitHub Desktop.
Simple arithmetic game
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# Arithmetic game where you have to multiply numbers in your head | |
# Choose up to n digits | |
# Ideas for game | |
# More digits as you progress, game gets harder incrementally | |
# Encode numbers a and b as 2x10 2,13,4x10 or 7-8x10 | |
# - Recursively parse 2-9,45-29x2 | |
# Hints after periods of inactivity, for example if asked to multiply 68x92, then a hint could be 90x8=720 | |
import random | |
import time | |
from collections import namedtuple, OrderedDict | |
operations = { | |
'+': lambda a, b: a+b, | |
'-': lambda a, b: a-b, | |
'*': lambda a, b: a*b, | |
} | |
Preset = namedtuple('Preset', 'name operation specific_numbers digits questions') | |
presets = OrderedDict([ | |
(1, Preset('multiply 1 digit (20 q)', '*', None, 1, 20)), | |
(2, Preset('multiply 1x2 digits (20 q)', '*', '1-10', 2, 20)), | |
(3, Preset('multiply 2 digits (20 q)', '*', None, 2, 20)), | |
(4, Preset('add 1 digit (20 q)', '+', None, 1, 20)), | |
(5, Preset('subtract 1x2 digit (20 q)', '-', None, 1, 20)), | |
]) | |
preset_text = '\n'.join([f'{k}: {v.name}' for k,v in presets.items()]) | |
def gen_random_number_factory(start=2, digits=1, rng=None, choices=None): | |
# Factory method | |
if choices: | |
return lambda: random.choice(choices) | |
elif rng: | |
return lambda: random.randrange(*rng) | |
else: | |
return lambda: random.randrange(start, 10**digits) | |
def get_generators(specific_numbers, digits): | |
if not specific_numbers: | |
gen_random_number_1 = gen_random_number_factory(digits=digits) | |
elif '-' in specific_numbers: | |
gen_random_number_1 = gen_random_number_factory(rng=[int(i) for i in specific_numbers.split('-')]) | |
else: | |
gen_random_number_1 = gen_random_number_factory(choices=[int(i) for i in specific_numbers.split()]) | |
gen_random_number_2 = gen_random_number_factory(digits=digits) | |
return gen_random_number_1, gen_random_number_2 | |
# The game itself | |
print('Welcome to the arithmetic game! You can choose a number of preset modes to begin with') | |
print(preset_text) | |
preset = input('Select a preset (hit enter if you want to customize) ') | |
if preset: | |
preset_num = int(preset) | |
operation_literal, specific_numbers, digits, rounds = presets[preset_num][1:] | |
else: | |
specific_numbers = input('Do you want to train with specific numbers? (default no) ') | |
digits = int(input('How many digits would you like? (default 1) ') or 1) | |
operation_literal = input('What operation would you like? (default *) ') or '*' | |
rounds = int(input('How many rounds would you like? (default 20) ') or 20) | |
# Determine which numbers and what operation | |
g1, g2 = get_generators(specific_numbers, digits) | |
operation = operations.get(operation_literal) | |
avg_time = 0 | |
correct = rounds | |
for k in range(rounds): | |
a = g1() | |
b = g2() | |
start = time.time() | |
ans = int(input(f'Round {k+1}/{rounds}: What is {a} {operation_literal} {b}? ')) | |
correct_ans = operation(a, b) | |
diff = time.time()-start | |
avg_time += diff/rounds | |
if ans != correct_ans: | |
correct -= 1 | |
print(f'Incorrect! the answer was {correct_ans}') | |
print(f'Your average time was {avg_time:.2f}s with {correct}/{rounds} correct answers') | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This game is a demonstration of some different tricks that you can do in Python.
Line 16 where we define the operations dictionary, is similar to a factory method which is demonstrated on line 33
OrderedDicts are used to number the presets, which are defined as NamedTuples on lines 23-30
On Line 31 we have a classic list-comprehension + join, on line 46 and 48 we also use list-comprehension
On line 60 we destructure (borrowed terminology from JS) the tuple into multiple variables, this is possible since the tuple is ordered
Line 62-65 we use the or operator to select a default value. It utilizes the fact that an empty string is returned when the user hits enter, i.e. default action, then
'' or value
will evaluate tovalue
.