#!/usr/bin/env python3
"""Repeatedly ask user randomized arithmetic questions."""
import abc
import operator
import random
import sys
class Arithmetic(metaclass=abc.ABCMeta):
"""Base class for arithmetic Q&A."""
@abc.abstractproperty
def name(self):
"""Human readable name of the operation."""
@abc.abstractproperty
def syntax(self):
"""Human readable operation string where {a} and {b} are replaced by
the first and second expression parameter respectively."""
@abc.abstractproperty
def op(self):
"""A function taking two int parameters and returning an int answer."""
@abc.abstractmethod
def get_parameters(self):
"""Return integer parameters a and b for a sample question."""
def ask(self):
"""Ask for the answer of a generated question."""
expression, answer = self.question()
print('\n>>>', expression)
self.interaction(expression, answer)
self.correct(expression, answer)
def question(self):
"""Return a question as a readable expression and its answer."""
a, b = self.get_parameters()
expression = self.syntax.format(a=a, b=b)
answer = self.op(a, b)
return expression, answer
def interaction(self, expression, answer):
"""Return when the user answers the given expression correctly."""
while True:
user_answer = int_input()
if answer == user_answer:
return
else:
self.incorrect()
@staticmethod
def correct(expression, answer):
print('Korrekt! {} = {}'.format(expression, answer))
@staticmethod
def incorrect():
print('Inte rätt. Försök igen!')
class Addition(Arithmetic):
"""Arithmetic question handler for addition."""
name = 'Addition'
syntax = '{a} + {b}'
op = operator.add
@staticmethod
def get_parameters():
"""Return integers a, b ∈ [1,20]."""
return random.randint(1, 20), random.randint(1, 20)
class Subtraction(Arithmetic):
"""Arithmetic question handler for subtraction."""
name = 'Subtraktion'
syntax = '{a} − {b}'
op = operator.sub
@staticmethod
def get_parameters():
"""Return integers a ∈ [10, 20], b ∈ [1,10]."""
return random.randint(10, 20), random.randint(1, 10)
class Multiplication(Arithmetic):
"""Arithmetic question handler for multiplication."""
name = 'Multiplikation'
syntax = '{a} ⋅ {b}'
op = operator.mul
@staticmethod
def get_parameters():
"""Return integers a, b ∈ [2, 12]."""
return random.randint(2, 12), random.randint(2, 12)
class Division(Arithmetic):
"""Arithmetic question handler for division."""
name = 'Division'
syntax = '{a} / {b}'
op = operator.floordiv
@staticmethod
def get_parameters():
"""Return integers a, b describing even integer division a / b with a
denominator and answer within [2, 12]."""
denominator = random.randint(2, 12)
numerator = denominator * random.randint(2, 12)
return numerator, denominator
def int_input(msg=''):
"""Ask user for input until an int is given, which is then returned."""
while True:
value = input(msg)
try:
return int(value)
except ValueError:
print('"{}" är inte ett heltal. Försök igen!'.format(value))
def choose_arithmetic(arithmetics):
"""Choose list item via user menu with an exit option."""
choice_fmt = '{}: {}'
for i, arithmetic in enumerate(arithmetics, start=1):
print(choice_fmt.format(i, arithmetic.name))
print(choice_fmt.format(0, 'Avsluta'))
try:
while True:
choice = int_input('Välj räknesätt: ')
if choice == 0:
quit()
try:
return arithmetics[choice - 1]
except IndexError:
print('Ogiltigt val. Försök igen!')
except (KeyboardInterrupt, EOFError):
quit()
def quit():
"""Exit the program with a user message."""
print('\nAvslutar programmet')
sys.exit()
def main():
arithmetics = [Addition(), Subtraction(), Multiplication(), Division()]
intro_msg = 'Testa dig i de olika räknesätten!'
print(intro_msg, '-' * len(intro_msg), sep='\n')
while True:
arithmetic = choose_arithmetic(arithmetics)
print('\nÖvar "{}" (Ctrl-C för nytt val).'.format(arithmetic.name))
try:
while True:
arithmetic.ask()
except (KeyboardInterrupt, EOFError):
print('\nAvslutar övning.\n')
continue
if __name__ == '__main__':
main()