Introduction
This game - a mathematical expression generation and solving challenge - is modelled on the logic of Wordle. The objective is to work through a sequence of five numbers and perform operations (+, -, *) to obtain the final result. Every third number is the result of an operation between the first and second numbers before that. The user has nine tries available, and for each attempt the game will provide a feedback. A number is reprinted in: green if the user guesses it in the right position; yellow if the position is wrong; black if it’s not present within the hidden expression. I hope it can be a fun and engaging way to test mathematical skills and simple logical thinking.
Packages deployed
- Random
- Tkinter
Next improvements
I plan to keep working on the code as I dive deeper in my learning journey. More specifically, I’d like to carry out the following updates:
- Three levels of difficulty: easy, medium, hard.
- General GUI improvements.
- General refactoring of the code.
Screenshots
Python code
import tkinter as tk
from tkinter import *
from tkinter import ttk
from tkinter import Text
import random
from tkinter import messagebox
################ SETTING UP THE GUI ######################
root = Tk()
root.title("Oprle")
root.geometry('600x600')
initialize_frame = tk.Frame(root)
game_page = tk.Frame(root)
def initialize_the_frame ():
initialize_frame = tk.Frame(root)
return initialize_frame
#root.iconbitmap('./mathematics.ico') #attribution: <a href="https://www.flaticon.com/free-icons/mathematics" title="mathematics icons">Mathematics icons created by Rabit Jes - Flaticon</a>
#mainframe = ttk.Frame(root,padding = "3 3 12 12")
root.columnconfigure(0,weight=1)
root.columnconfigure(1,weight=1)
root.columnconfigure(2,weight=1)
initialize_frame.grid(column=1,row=0)
################ GAME LOGIC ##########################
operation_to_send = StringVar()
hidden_operation = []
operation_to_check = StringVar()
def create_operation():
operation = []
numbers = [1,2,3,4,5,6,7,8,9]
operators = ['+', '-', '*']
a = random.choice(numbers)
b = random.choice(numbers)
c= random.choice(numbers)
d = random.choice(numbers)
e = random.choice(numbers)
f = random.choice(numbers)
g = random.choice(numbers)
h = random.choice(numbers)
random_numbers = [a,b]
i = 1
operation.append(a) #number
operation.append(random.choice(operators))
operation.append(b) #number
first_operator = eval(''.join(map(str, str(a)+ str(operation[1]) + str(b))))
operation.append(random.choice(operators))
operation.append(first_operator) #number
operation.append(random.choice(operators))
second_operator = eval(''.join(map(str, str(b)+ str(operation[3]) + str(first_operator))))
operation.append(second_operator) #number
operation.append(random.choice(operators))
third_operator = eval(''.join(map(str, str(first_operator)+ str(operation[5]) + str(second_operator))))
operation.append(third_operator)
operation.append("=")
result_ = eval(''.join(map(str, str(second_operator)+ str(operation[7]) + str(third_operator))))
operation.append(result_)
empty_space = " _"
operation_to_check.set(''.join(map(str,operation)))
hidden_operation.append(str(operation[0]) + empty_space + " " + str(operation[2]) + empty_space + empty_space + empty_space + empty_space + empty_space + empty_space + " = " + "_ ")
print(operation)
print(operation_to_send.get())
return operation_to_send.set(''.join(map(str, hidden_operation)))
create_operation()
operation_widget = Label(game_page, text=operation_to_send.get(),foreground="black",font=('Calibri',20))
text_widget = Text(game_page, height=10, width=40)
################### GUI ELEMENTS ######################
label = Label(initialize_frame, text="Oprle", foreground="black",font=('Calibri',40),pady=10)
rules = Label(initialize_frame, text="""
Welcome to Oprle!
The rules are the following: this program will generate a mathematical expression
consisting of 5 (five) numbers and the following mathematical operators:
+, -, *
(i.e., addition, subtraction, moltiplication).
The 3rd (third) number is the result of the operation between
the 1st (first) and 2nd (second) number.
In the following example 1 is the 1st number, 4 is the 2nd, and 5 is the 3rd, obtained
by applying the operation between the first and the second number (+):
1 + 4 _ 5
Once you obtain the 3rd (third) number, you can proceed with finding the 4th (fourth) one,
which is the result of the operation between the 2nd (second)
and 3rd (third) number, for instance 12 is the result of 4 * 3:
1 + 4 * 3 _ 12
This goes on till you reach the 5th (fifth) number. Once you have it, you can find
the final result by guessing the
operation between the 4th (fourth) and the 5th (fifth) number, which you will need to
add after the '=' symbol to complete the game.
""")
############## GUI FUNCTIONS ################
def initialize_clicked():
# Hide label and rules
label.grid_forget()
rules.grid_forget()
initialize.grid_forget()
initialize_the_frame()
def show_frame(frame,current_frame):
current_frame.grid_forget()
frame.grid(column=1,row=0)
frame.tkraise()
def show_message():
question = messagebox.showinfo(title="Congrats!",message="Congrats! You got the right operation!")
def error():
errore = messagebox.showinfo(title="Too many trials!",message="Oh no, it looks like this one was really difficult!\n Try again:)")
def reset_wigets():
for widget in game_page.winfo_children:
if isinstance(widget,tk.Entry):
widget.delete(0,'end')
if isinstance(widget, tk.StringVar):
widget.set("")
if isinstance(widget, tk.Label):
widget.grid_forget()
if isinstance(widget, tk.Text):
widget.delete('1.0', tk.END)
initialize_frame.grid(column=1,row=0)
############## GUI WORKFLOW ##################
guesses = []
extra_lines = []
current_guess_number = 0 # Initialize once outside the function
def restart_game():
initialize_frame.grid(row=0, column=1, sticky="nsew")
# Reset any variables or data structures
guesses.clear()
extra_lines.clear()
current_guess_number = 0
game_page.grid_forget()
initialize_the_frame()
print("cleared")
# Defining colors
correct_color = "green"
partial_match_color = "yellow"
default_color = "black"
labels = []
def submit_guess():
global current_guess_number
global extra_lines
print("submit_guess called")
guesses.append(attempt.get()[:10])
if current_guess_number < len(guesses):
entry_guess = guesses[current_guess_number]
print("Length of extra_lines:", len(extra_lines))
for i, line in enumerate(extra_lines):
print(f"Updating line {i} with guess {current_guess_number}")
if i == current_guess_number:
# Remove the existing Label widget (line)
line.grid_forget()
# Create a Text widget
text_widget = Text(game_page, height=1, width=40, font=('Calibri', 14))
text_widget.grid(column=1,row=i+4)
# Apply tags for coloring
text_widget.tag_configure('correct', foreground='green')
text_widget.tag_configure('partial_match', foreground='yellow')
text_widget.tag_configure('default', foreground='black')
for l in range(min(len(attempt.get()), len(operation_to_check.get()))):
if attempt.get()[l] == operation_to_check.get()[l]:
text_widget.insert('end', attempt.get()[l], 'correct')
elif attempt.get()[l] in operation_to_check.get():
text_widget.insert('end', attempt.get()[l], 'partial_match')
else:
text_widget.insert('end', attempt.get()[l], 'default')
if attempt.get() == operation_to_check.get():
show_message()
quit.grid(column=1,row=16)
#guesses = []
#extra_lines = []
#current_guess_number = 0
#create_operation()
# Save the new Text widget to the list
extra_lines[i] = text_widget
current_guess_number += 1
print(current_guess_number)
if current_guess_number == 9:
error()
quit.grid(column=1,row=16)
content = text_widget.get("1.0", "end-1c")
print(content)
user_input_entry.delete(0, END) # Clear the entry field
###### Buttons and fields Intro page ###
initialize = Button(initialize_frame, text="Let's play!", background="green",command=lambda: show_frame(game_page,initialize_frame), foreground="white")
quit = Button(game_page,text="Quit",background="red",foreground="white",command=lambda: root.destroy())
#### Buttons and fields Game page ####
entry_guess = ""
game_title = Label(game_page, text="Oprle", foreground="black",font=('Calibri',40),pady=10)
entry_intro = Label(game_page, text="Here's an operation for you to crack:",font=('Calibri',14))
equation_label = Label(game_page, textvariable=operation_to_send, padx=5,pady=10)
attempt = StringVar()
user_input_entry = ttk.Entry(game_page, textvariable=attempt, font=('Calibri', 12))
submit_button = ttk.Button(game_page, text="Submit", command=submit_guess)
root.bind('<Return>', lambda event: submit_guess())
congrats = ttk.Button(game_page,text=f"\n Congrats! You got the right operation in {current_guess_number} trials!", command=show_message)
for _ in range(8):
line = Label(game_page, text="_ _ _ _ _ _ _ _ _ _ _", font=('Calibri', 14), padx=5, pady=5)
#text_widget = Text(game_page, height=1, width=40, font=('Calibri', 14))
extra_lines.append(line)
empty_line = Label(game_page,text=" ",font=('Calibri', 14),padx=5,pady=2)
###### Intro Page #####
label.grid(column=1,row=0)
rules.grid(column=1,row=1)
print("")
initialize.grid(column=1,row=2)
###### Game Page #####
game_title.grid(column=1,row=0)
entry_intro.grid(column=1,row=1)
operation_widget.grid(column=1,row=2)
for i, line in enumerate(extra_lines):
line.grid(column=1,row=i+3) # Assuming you're using grid layout
empty_line.grid(column=1,row=12)
user_input_entry.grid(column=1,row=13)
user_input_entry.focus()
submit_button.grid(column=1,row=15)
root.mainloop()