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 03:12:30 EDT 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
Answered: 3. Correct.
Problem #2
3 + 3 = 6
Answered: 6. Correct.
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
Answered: 1. Correct.
Problem #1
8 - 6 = 14
Answered:14. Wrong. Correct
answer: 2
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()
self.asked = self.correct = 0
self.set_asked = set()
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
then asks the question and checks the answer"""
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...
if problem in self.set_asked:
continue
self.set_asked.add(problem)
#answers other than digits will be ignored and the prompt
will repeat
while True:
try:
prompt = "\n\t\t" + problem + " = "
self.writer(prompt,False)
answer = raw_input(prompt)
answer = int("%s" % (answer.strip()))
except ValueError:
continue
break
self.asked += 1
#good, or bad, answer?
if (answer == solution):
msg = GOOD_ANSWER % {"answer":answer}
self.correct += 1
else:
msg = BAD_ANSWER % locals()
self.writer(msg,True)
return
#not exactly i18n, but it's a start
GOOD_ANSWER = "\t\t\t\t\tAnswered:%(answer)2s. Correct."
BAD_ANSWER = "\t\t\t\t\tAnswered:%(answer)2s. Wrong.
Correct answer:%(solution)s"
GREETING = "Hello, I am your friendly computer. I have a
math quiz for you"
FAREWELL = "\n\nCongratulations, you got %(correct)2s right
out of %(asked)2s"
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"
parser.add_option("-1",action="store",type="int",default=default,dest="numberRange",help=HELP_NUMBER1
% (default))
parser.add_option("-2",action="store",type="int",dest="numberRange2",help=HELP_NUMBER2)
default = "+-"
parser.add_option("-
o",action="store",dest="operators",default=default,help=HELP_MATH_OPERATORS
% default)
default = "20"
parser.add_option("-
r",action="store",type="int",dest="repetitions",default=default,help=HELP_REPETITIONS
% default)
default = "0"
parser.add_option("-
L",action="store",type="int",dest="minSolution",default=default,help=HELP_MIN_SOLUTION
% default)
default = "20"
parser.add_option("-
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()
More information about the Python-list
mailing list