# Simple Addition and Subtraction Game in Python

Please give me some pointers on my first Python program!

Points of interest:

• Within the calculate() function, I wanted to return correctAnswer in order to avoid using it as a global variable. When passing calculate to isCorrect(), I would get a TypeError: missing 1 required positional argument. After a ton of searching I wasn't able to avoid using the global variable.
• This leads me to the point on global variables: I tried my darnest to avoid using them across the board but I must be missing something obvious that's preventing me from understanding how to do so.
• I will be honest, I didn't place much time on the playAgain() function but not because I didn't want to--I just didn't have many ideas of what I wanted. Any pointers on a better "restart" button are welcomed.

    from tkinter import Entry
import tkinter as tk
import random as rand
import sys
import os

root = tk.Tk()
root.geometry("450x250+500+300")
root.title("Let's play Math!")

welcomeLabel = tk.Label(text="LET'S PLAY MATH!").pack()
startLabel = tk.Label(text='Select a math operation to start').pack()

def genRandom():
a = rand.randrange(1,10,1)
b = rand.randrange(1,10,1)

return a, b

def calculate(method):

ranNumber = genRandom()

calcString = "+"
elif method == "Sub":
calcString = "-"

QuestionLbl = tk.Label(text="What does {0} {1} {2} equal?".format(ranNumber, calcString, ranNumber))
QuestionLbl.place(x=0, y=125)

isCorrectBtn.place(x=300, y = 150)
def isCorrect():

restartBtn = tk.Button(text="Play Again?", command=playAgain)
restartBtn.place(x=300, y = 200)

def playAgain():
python = sys.executable
os.execl(python, python, * sys.argv)

subBtn = tk.Button(text="Subtraction", command=lambda: calculate("Sub"))
subBtn.place(x=220, y=60)

tk.mainloop()


Through some research I've updated the following:

Thanks to @Shule, I've settled on using the code below. I added some comments of my own, moved some stuff around to try to make a bit more sense of what's going on, and also created the rightWrongTxt string. However, @Shule deserves most of the credit for making this code better.

import tkinter as tk
import random as rand

class Game:
def __init__(self):
# Initializing the widgets and the variables you want to access from multiple methods

self.root = tk.Tk()

#defining actual dimensions of the window
self.root.geometry("450x250+500+300")

#window title; does not require .pack()
self.root.title("Let's play Math!")

#LABELS
#.pack() will display labels in the middle of the screen
self.welcomeLbl = tk.Label(text="LET'S PLAY MATH!")
self.welcomeLbl.pack()

self.startLbl = tk.Label(text='Select a math operation to start')
self.startLbl.pack()

self.QuestionLbl = tk.Label()

self.rightWrongLbl = tk.Label()
self.rightWrongTxt = tk.StringVar()

#BUTTONS

self.subBtn = tk.Button(text="Subtraction", command=lambda: self.calculate("Sub"))
self.subBtn.place(x=225, y=60)

self.restartBtn = tk.Button(text="Play Again?", command=self.playAgain)

#INPUT BOX

def genRandom(self):
a = rand.randrange(1,10,1)
b = rand.randrange(1,10,1)

return a, b

def calculate(self, method):

self.subBtn.config(state='disabled')

randNum = self.genRandom()

calcString = "+"
elif method == "Sub":
calcString = "-"

#Question generation
self.QuestionLbl.config(text="What does {0} {1} {2} equal?".format(randNum, calcString, randNum))
self.QuestionLbl.place(x=0, y=125)

self.isCorrectBtn.place(x=300, y=150)

def isCorrect(self):
try:
except ValueError:
self.rightWrongTxt.set("Non-numeric input. \nEnter numbers only.")
self.rightWrongLbl.config(textvariable = self.rightWrongTxt)
self.rightWrongLbl.place(x=150, y=125)

self.rightWrongTxt.set("Let's try again.")
self.rightWrongLbl.config(textvariable = self.rightWrongTxt)
self.rightWrongLbl.place(x=150, y=125)
self.rightWrongTxt.set("Hooray!")
self.rightWrongLbl.config(textvariable = self.rightWrongTxt)
self.rightWrongLbl.place(x=150, y=125)

self.restartBtn.place(x=300, y=200)

def playAgain(self):

self.rightWrongLbl.config(text="Let's try again.")

self.subBtn.config(state='active')

self.QuestionLbl.place_forget()
self.isCorrectBtn.place_forget()
self.rightWrongLbl.place_forget()
self.restartBtn.place_forget()

if __name__ == '__main__':
game = Game()
game.root.mainloop()

• Thanks @Shule, when I first started this project I had done so. However, as I began to encounter issues and searching for answers I ended up not using a class. I still have that other code; I'll refactor and post(might take me a bit). Now I know. – Nahuatl_C137 Jan 23 '18 at 4:28

• Make sure the focus is in a convenient place (in this case, it would be nice if it were always in the Entry widget; that way, we wouldn't have to click on it).
• Your buttons are overlapping on my screen (the subtraction button begins before the addition button is finished).
• If you could avoid the need to restart the program, that would be great (in some cases it might be important, but I don't think this is one of them). You can just show/hide and reset the widgets.
• I would suggest consolidating the program into a class, for readability, organization, and ease of access. That way, you can define all your widgets in one place, and access them (as well as any needed variables) from everywhere in the class (via self, which is the first parameter of every method, although you can technically name it whatever you want instead of 'self'). You don't need any global variables this way. If you're new to object-oriented programming, you'll want to learn up on classes and objects to know more about how this works. It's just another way to organize your code to make things convenient. The __init__ method is the constructor (which executes when the game object is created.
• Check to make sure the answer is an integer (if it only takes integers for input).
• I didn't fix it in the code below, but you'll want to change some variable names for the sake of naming conventions. For instance, some of those are capitalized that shouldn't be. The code will still run, but it'll be weird for many people. You might want to check out PEP 8 for a big list of conventions (some of which I probably don't follow, but perhaps should, like the maximum line length).

There are other improvements for fine-tuning that I could probably suggest, but this is my answer for now. Here's the amended code:

import tkinter as tk
import random as rand

class Game:
def __init__(self):
#Initializing the widgets and the variables you want to access from multiple methods
self.root = tk.Tk()
self.root.geometry("450x250+500+300")
self.frame = tk.Frame(self.root)
self.frame.pack()
self.root.title("Let's play Math!")
self.root.welcomeLabel = tk.Label(text="LET'S PLAY MATH!").pack()
self.startLabel = tk.Label(text='Select a math operation to start').pack()
self.subBtn = tk.Button(text="Subtraction", command=lambda: self.calculate("Sub"))
self.subBtn.place(x=235, y=60)
self.restartBtn = tk.Button(text="Play Again?", command=self.playAgain)
self.QuestionLbl = tk.Label()
def genRandom(self):
a = rand.randrange(1,10,1)
b = rand.randrange(1,10,1)
return a, b
def calculate(self, method):
self.playAgain() #This is to clear the widgets if you press one of the buttons before you give a correct answer
ranNumber = self.genRandom()
calcString = "+"
elif method == "Sub":
calcString = "-"
self.QuestionLbl.config(text="What does {0} {1} {2} equal?".format(ranNumber, calcString, ranNumber))
self.QuestionLbl.place(x=0, y=125)
self.isCorrectBtn.place(x=300, y = 150)
def isCorrect(self):
try:
except ValueError:
#If it's not an integer, you get a ValueError, which we catch to prevent the program from producing an error.
#You could make another widget that displays some kind of response here when they put in input that doesn't compute, but I'm just making it so it doesn't respond to it here.
return
self.restartBtn.place(x=300, y = 200)
def playAgain(self):
#We're hiding the appropriate widgets and clearing the text of the Entry widget.