Skip to content

Instantly share code, notes, and snippets.

@tclancy
Last active April 11, 2022 16:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tclancy/a3f9b9b123b0fcdcebee0ac92f3591f2 to your computer and use it in GitHub Desktop.
Save tclancy/a3f9b9b123b0fcdcebee0ac92f3591f2 to your computer and use it in GitHub Desktop.
Simple math problems for practice
#!/usr/bin/env python3
#-*- coding: utf-8 -*-
from functools import reduce
import operator
from random import randrange
from typing import Sequence
PROMPT = "What's the answer? "
GOODBYE = "\n\n *** Thanks for playing! ***"
# colors - stolen from https://stackoverflow.com/a/287944/7376
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
def random_problem(minimum: int, maximum: int,
number_of_terms: int, sign: str) -> None:
"""
Generate a series (of length number_of_terms) of numbers between
minimum and maximum for the given problem type and check the work
"""
if number_of_terms < 2:
raise SystemExit(f"Looks like someone made a mistake here! "
f"Please fix the difficulty: terms for {sign}")
terms = sorted(
[randrange(minimum, maximum) for _ in range(number_of_terms)],
key=int, reverse=True
)
math_operation = {
"+": operator.add,
"x": operator.mul,
"/": operator.truediv,
"-": operator.sub
}[sign]
format_problem(sign, *terms)
answer = input(PROMPT)
attempt = 1
correct_answer = reduce(lambda x, y: math_operation(x, y), terms)
while not get_result(answer, correct_answer):
answer = input(PROMPT)
attempt += 1
if attempt % 3 == 0:
get_hint(sign, terms, correct_answer)
def get_result(attempt: str, answer: int) -> bool:
"""
See if the user got the correct answer or if they want to quit or skip
"""
try:
if int(attempt) == answer:
print(f"{bcolors.OKGREEN}Correct! Let's try another!\n{bcolors.ENDC}")
return True
except ValueError:
inp = attempt.lower()
if inp == "q":
raise SystemExit(GOODBYE)
if inp == "s":
print("No problem, on to the next one!")
return True
print(f"{bcolors.FAIL}Please type a number!{bcolors.ENDC}")
return False
print(f"{bcolors.WARNING}Try again{bcolors.ENDC}")
return False
def get_hint(sign: str, terms: Sequence[int], correct_answer: int) -> None:
"""
In the most horribly sloppy fashion, provide hints for the problem type.
If the answer is less than 1 (because the author made 0 attempt to prevent
it) give the poor dummy an out.
"""
if correct_answer < 1:
print(f"{bcolors.WARNING}Hmm, this looks like a hard one. "
f"Type {bcolors.OKCYAN}s {bcolors.WARNING}to skip{bcolors.ENDC}")
return
if sign == "+":
if max(terms) > 10:
print("Slow down and write it out on your whiteboard or paper")
else:
print("If I have " + terms[0] * "🍎" + " and someone gives me")
for t in terms[1:]:
print(t * "🍎")
print("How many apples do I have?")
if sign == "-":
print("The answer to subtraction is the difference between the numbers. Maybe this will help")
for i, t in enumerate(terms):
color = bcolors.OKBLUE if i % 2 else bcolors.OKGREEN
print(color + "." * t + bcolors.ENDC)
if sign == "/":
print("You don't know how to do this yet! Stop guessing :)")
if sign == "x":
x, y = terms[:2]
print(f"Think of this as {y} groups of {x}")
for _ in range(y):
print("." * x)
def format_problem(sign: str, *args):
# TODO: stop cheating and handle right-padding
max_width = len(str(max(args)))
for a in args[:-1]:
print(f" {a}")
print(sign + str(args[-1]))
print("-" * (max_width + 1))
"""
Set the level of difficulty per type of problem
"""
difficulty = {
"+": {
"minimum": 10,
"maximum": 90,
"number_of_terms": 2
},
"-": {
"minimum": 10,
"maximum": 90,
"number_of_terms": 2
},
"x": {
"minimum": 1,
"maximum": 9,
"number_of_terms": 2
},
"/": {
"minimum": 1,
"maximum": 9,
"number_of_terms": 2
}
}
if __name__ == '__main__':
print("Welcome to the Mathalympics, let's see how you're doing")
print("Press q to quit and you can always press s to skip if you're stuck")
print("Do you want to do Addition (+), Subtraction (-), Multiplication (x) or Division (/)?")
sign = None
while not sign:
choices = difficulty.keys()
choice = input(f"Enter {' or '.join(choices)} ")
if choice in choices:
sign = choice
try:
while True:
kwargs = difficulty[sign]
kwargs["sign"] = sign
random_problem(**kwargs)
except KeyboardInterrupt:
print(GOODBYE)
@tclancy
Copy link
Author

tclancy commented Feb 9, 2021

Should make reverse=True on the sorting an optional parameter by operation type as things like fractions will want to be sorted in ascending order.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment