# funny little 190 lines command line math tutor I wrote for my 7-year old.

DouhetSukd at gmail.com DouhetSukd at gmail.com
Fri Sep 14 09:12:30 CEST 2007

```Sadly lacking in multi-media bells and whistles.  But my daughter
actually likes playing with it.

Coded on windows, but no reason it shouldn't work on Linux/OS X.

(hopefully the indentation won't be too mangled by usenet.  apologies
in advance if that is the case)

Enjoy.

Sample session:

D:\user\python>mathtester.py -1 3 -o /+ -r 2
Hello, I am your friendly computer.  I have a math quiz for you

Problem #1

1 +  2 = 3

Problem #2

3 +  3 = 6

Congratulations, you got  2 right out of  2

code:

************************************
"""
mathtester.py -h for more help .

HELPS YOUR KIDS TO LEARN MATH WITH INTERACTIVE PROMPTS.  EXAMPLE:

Hello, I am windoze, your friendly computer.  I have a math quiz for
you

Problem #0

4 -  3 = 1

Problem #1

8 -  6 = 14

This will pick two numbers, each between 0 and 10.  It will either add
or substract them.  Any questions with solutions less than zero
or greater than 20 will be rejected and re-created.

You get this by running it with all defaults.  i.e.

c:\>python mathtester.py

to get something like doing the multiplication of 1 to 3 times 10, run

c:\>python mathtester -o * -1 3 -2 10 -H 30

only multiplication '*' will be used, number #1 will be between 0 and
3.  number #2 will be between 0 and 10.  answers greater than 30 will
be rejected.

"""

import os, sys, optparse, random

_module_doc = __doc__

class Writer:
"""write to file and output to console"""
def  __init__(self,fout):
self.fout = fout
def __call__(self,s,echoconsole=True):
self.fout.write("\n" + s.replace("\t","    "))
if echoconsole:
print s

class Worker:

fout = None
filename = "test.txt"
trivial_tolerance_percentage = 40
accepted_operators = "+-*"

def formatoptions(self):
"""most of the work is already done by optparser"""
#filter on acceptable operators.  exit if none found.
self.operators = [i for i in self.options.operators if i in
self.accepted_operators]
if not self.operators:
print ERROR_MSG_OPERATORS % {"supported" :
self.accepted_operators}
sys.exit()
#both number range limits are equal.
if not self.options.numberRange2:
self.options.numberRange2 = self.options.numberRange

def  __init__(self):
self.fout = open(self.filename,"w")
Worker.writer = Writer(self.fout)
self.formatoptions()

def process(self):
"""main loop - prints greetings, loops on questions, print
result summary and exits"""
try:
self.writer(GREETING)
[self.doTest(cntr) for cntr in
range(0,self.options.repetitions)]
except KeyboardInterrupt:
pass
self.writer(FAREWELL % vars(self))
return True

def doTest(self,number):
"""the workhorse function.  randomly picks a question + vets
the solution
self.writer("\n Problem #%s" % (number+1))

while True:
#pick random numbers and operator
number1  = random.randint(0,self.options.numberRange)
number2  = random.randint(0,self.options.numberRange2)
operator =
self.operators[random.randint(0,len(self.operators)-1)]

if number1 < 2 or number2 < 2 and
random.randint(1,100)>self.trivial_tolerance_percentage:
#potentially reject trivial problems consisting of
zeros and ones.
continue

#flip a coin and put number #1 either right or left -
#remember that the number ranges can be unequal so there
is a difference
if random.randint(1,2) % 2:
problem  = "%2s %s %2s" % (number1,operator,number2)
else:
problem  = "%2s %s %2s" % (number2,operator,number1)

#use eval to get the expected solution
solution = eval(problem)

#solution within accepted bounds? if not, build another
problem
if not ((solution >= self.options.minSolution) and
(solution <= self.options.maxSolution)):
continue

#don't ask the same question multiple times...
continue

#answers other than digits will be ignored and the prompt
will repeat
while True:
try:
prompt = "\n\t\t" + problem + " = "
self.writer(prompt,False)
except ValueError:
continue
break

self.correct += 1
else:
self.writer(msg,True)
return

#not exactly i18n, but it's a start
GREETING            = "Hello, I am your friendly computer.  I have a
math quiz for you"
FAREWELL            = "\n\nCongratulations, you got %(correct)2s right

HELP_NUMBER1        = "max value for number #1.  this keeps things
from getting too hard or easy. default:%s"
HELP_NUMBER2        = """max value for number #2, defaults to use the
same as number #1.  the reason you may want to have different ranges
is say doing the table of multiplications for 1 to 3 times 10"""
HELP_MATH_OPERATORS = "math operators to use/  defaults: %s"
HELP_REPETITIONS    = "number of questions to ask [%s]"
HELP_MIN_SOLUTION   = "minimum solution you want  [%s].  used to have
simple or hard questions"
HELP_MAX_SOLUTION   = "maximum solution you want  [%s].  used to have
simple or hard questions"

ERROR_MSG_OPERATORS = "no valid operators provided.  supported
operators: %(supported)s"

def getParser():
"""use optparse to prompt on the command line"""
parser = optparse.OptionParser(usage = _module_doc)

default = "10"

% (default))

default = "+-"
o",action="store",dest="operators",default=default,help=HELP_MATH_OPERATORS
% default)

default = "20"
r",action="store",type="int",dest="repetitions",default=default,help=HELP_REPETITIONS
% default)

default = "0"
L",action="store",type="int",dest="minSolution",default=default,help=HELP_MIN_SOLUTION
% default)

default = "20"
H",action="store",type="int",dest="maxSolution",default=default,help=HELP_MAX_SOLUTION
% default)

return parser

def main():
worker = success = 0
try:
(Worker.options, Worker.args) =
getParser().parse_args(sys.argv[1:])
worker = Worker()
success = worker.process()
finally:
if worker and worker.fout:
worker.fout.close()
if success :
os.startfile(Worker.filename)

if __name__ == "__main__":
main()

```