Week 5 A Major Project
Math Game
Overview
- This assignment will incorporate both python algorithm development and GUI design using Glade into one project
- The goal is to create an educational program that tests and sharpens arithmetic skill and speed
- Very similar to the Brain Age game on the Nintendo DS or Brain Tuner on the iPhone
- Your program should look something like (but not exactly the same as the picture shown above)
Steps
- Find a way to generate simple arithmetic equations (limit your operands to 0-10) that may be correct or wrong. and display them one by one
- Accept input from the user (example 'y' and 'n') to say whether they think the equations are right or not and print a message indicating if they gave a right answer
- Your wrong inputs may be off by 1 or 2 for addition and subtraction or up to multiples of one of the operands for multiplication
- Try to make them 'look correct'
- Use the python random library to generate randomness in your program
- Each gaming session may contain up to 20 questions and the user's score should be displayed to the at the end of the session
- Here are some examples:
\begin{align} $5\times7=35$ \\* $6\times8=48$ \\* $6\times3=16$ \\* $8\times3=5$ \\* $9\times9=18$ \\* $1\times8=-7$ \\* $4\times4=13$ \\* $6\times3=6$ \\* $8\times7=-1$ \\* $3\times9=-6$ \\* $6\times8=15$ \end{align}
Suggested milestones
- Working random equation generator
- Command-line implementation of program
- Successful display of equations on screen sequentially
- Final Program
Teacher solution
- Backend code: brainage.py
#!/usr/bin/env python
import random
import time
import sys
try:
import pygtk
pygtk.require("2.0")
except:
pass
try:
import gtk
import gtk.glade
except:
sys.exit(1)
class BrainAge:
def getFakeAdd(self,a,b):
offset=random.randint(1,3)
neg=random.randint(0,1)
if neg==0:
return a+b+offset
else:
return a+b-offset
def getFakeSub(self,a,b):
offset=random.randint(1,3)
neg=random.randint(0,1)
if neg==0:
return a-b+offset
else:
return a-b-offset
def getFakeMult(self,a,b):
while True:
offset=random.randint(1,2)
neg=random.randint(0,1)
which=random.randint(0,1)
if which==0:
if neg==0:
answ = (a+offset)*b
else:
answ = (a-offset)*b
else:
if neg==0:
answ = a*(b+offset)
else:
answ = a*(b-offset)
if answ != (a*b):
return answ
def __init__(self):
#Set the Glade file
self.started=False
self.gladefile = "brainage.glade"
self.wTree = gtk.glade.XML(self.gladefile)
self.scrolledproblems=self.wTree.get_widget("scrolledproblems")
self.statuslabel=self.wTree.get_widget("statuslabel")
#Create our dictionay and connect it
dic = { "on_start_clicked" : self.startGame,
"on_BrainAge_key_press_event" : self.keyPressed,
"on_MainWindow_destroy" : gtk.main_quit }
self.wTree.signal_autoconnect(dic)
def keyPressed(self, widget, event):
if self.started != True:
return
if self.problem_num > self.num_questions:
return
inp=gtk.gdk.keyval_name(event.keyval)
if (self.fake==1 and inp=='n') or (self.fake==0 and inp=='y'):
self.right_questions += 1
templabel=gtk.Label("O")
self.problembox.pack_end(templabel)
templabel.show()
self.problembox.show()
self.problemsvbox.show()
else:
templabel=gtk.Label("X")
self.problembox.pack_end(templabel)
templabel.show()
self.problembox.show()
self.problemsvbox.show()
if self.problem_num==self.num_questions:
self.finish_time = time.time()
self.statuslabel.set_text("You got "+str(100*self.right_questions/self.num_questions)+"% right in "+str(self.finish_time-self.init_time)+" seconds.")
self.statuslabel.show()
self.started=False
else:
self.nextProblem()
def nextProblem(self):
self.statuslabel.set_text("Problem #" + str(self.problem_num+1) + " of " + str(self.num_questions) + ".")
a = random.randint(0,10)
b = random.randint(0,10)
choice = random.randint(0,2)
self.fake=random.randint(0,1)
if choice == 0:
if self.fake==0:
answ = a+b
else:
answ=self.getFakeAdd(a,b)
operation = "+"
elif choice == 1:
if self.fake==0:
answ=a-b
else:
answ=self.getFakeSub(a,b)
operation = "-"
else :
if self.fake==0:
answ=a*b
else:
answ=self.getFakeMult(a,b)
operation = "x"
templabel=gtk.Label(str(a)+" "+operation+" "+str(b)+" = "+str(answ)+" ")
templabel.set_size_request(400,100)
self.problembox=gtk.HBox()
self.problembox.pack_start(templabel)
self.problemsvbox.pack_end(self.problembox)
templabel.show()
self.problembox.show()
self.problemsvbox.show()
self.problem_num+=1
def startGame(self,widget):
self.init_time = time.time()
self.num_questions = 15
self.problem_num = 0
self.right_questions = 0
self.started=True
self.problemsvbox=gtk.VBox()
self.scrolledproblems.add_with_viewport(self.problemsvbox)
self.problemsvbox.show()
self.nextProblem()
#def btnHelloWorld_clicked(self, widget):
# print "Hello World!"
if __name__ == "__main__":
hwg = BrainAge()
gtk.main()
- Glade file: brainage.py
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="BrainAge">
<property name="width_request">640</property>
<property name="height_request">480</property>
<property name="visible">True</property>
<property name="title" translatable="yes">BrainAge</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<signal name="key_press_event" handler="on_BrainAge_key_press_event" last_modification_time="Sun, 19 Oct 2008 23:15:01 GMT"/>
<signal name="destroy" handler="on_MainWindow_destroy" last_modification_time="Sun, 19 Oct 2008 23:45:09 GMT"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledproblems">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkLabel" id="statuslabel">
<property name="visible">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Start!</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_start_clicked" last_modification_time="Sun, 19 Oct 2008 23:01:45 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>
page revision: 21, last edited: 17 Feb 2009 14:29