Skip to content

Instantly share code, notes, and snippets.

@horstjens
Created June 11, 2022 09:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save horstjens/49cc1bf6117d9d03788dd3826727eb48 to your computer and use it in GitHub Desktop.
Save horstjens/49cc1bf6117d9d03788dd3826727eb48 to your computer and use it in GitHub Desktop.
tower_of_hanoi
import PySimpleGUI as sg
import random
import time
WIDTH, HEIGHT = 600,200
HELPTEXT = """GOAL: move all discs from stack 1 (left) to stack 3 (right)
RULES:
1.) Move only one disk at a time.
2.) You can only remove a disk from the top of a stack and place it on the top of another.
3.) A larger disk can not be placed on top of a smaller disk."""
COLORS = []
for r in range(16,200,8):
for g in range(16,200,8):
for b in range(16,200,8):
COLORS.append("#{}{}{}".format(str(hex(r).replace("0x","")),
str(hex(g).replace("0x","")),
str(hex(b).replace("0x","")),
))
#print(COLORS)
random.shuffle(COLORS)
# -------- first gui: theme cooser --------------------------
sg.theme(random.choice(sg.theme_list()))
# ---------------------- theme selector layout -------------
layout1 = [
[
sg.Text("Select theme:"),
sg.Combo(
values=sg.theme_list(),
default_value=sg.theme(),
enable_events=True,
key="selector",
),
],
[sg.Text("# of discs:"),sg.Slider(range=(3,16), default_value=5,orientation="h", key="disc_slider")],
[sg.Text("Pause in [ms]:"),sg.Slider(range=(0,1000), default_value=500,orientation="h", key="pause_slider")],
[sg.Button("play the game")],
]
window1 = sg.Window("theme selector", layout1)
# ---------------- main loop for first layout ---------------
while True:
event1, values1 = window1.read()
if event1 == "play the game" or event1 == sg.WIN_CLOSED:
# sg.theme("DarkBlue3")
break
if event1 == "selector":
old = sg.theme()
sg.theme(values1["selector"])
if sg.PopupYesNo("do you like this Theme?") == "Yes":
break
else:
sg.theme(old)
NUMBER_OF_DISCS = int(values1["disc_slider"])
PAUSE= int(values1["pause_slider"])/1000
window1.close()
# -----------------end of theme chooser -------------------
# -----------------second gui: tower of hanoi game -----------
layout2 = [
[sg.Text("Towers of Hanoi")],
[sg.Multiline(default_text = HELPTEXT, size=(90,5))],
[sg.Graph(canvas_size = (WIDTH,HEIGHT), graph_bottom_left=(0,0), graph_top_right=(WIDTH,HEIGHT), key="canvas",background_color="#FFFFFF")],
[sg.Button("1>>2",size=(8,1)),sg.Button("1>>3",size=(8,1)),sg.Sizer(20,0),
sg.Button("1<<2",size=(8,1)),sg.Button("2>>3",size=(8,1)),sg.Sizer(20,0),
sg.Button("1<<3",size=(8,1)),sg.Button("2<<3",size=(8,1)),],
[sg.Button("Exit"), sg.Button("Solve"), sg.Button("Reset"), sg.Button("Log")],
]
window2 = sg.Window("enjoy the game", layout2)
window2.finalize() # necessary to be able to draw on canvas
c = window2["canvas"]
total_y = NUMBER_OF_DISCS + 3
border = c.draw_rectangle((0,HEIGHT),(WIDTH,0),line_color="red",line_width=2)
rod1 = c.draw_line((WIDTH/4*1,0), (WIDTH/4*1,HEIGHT/total_y*(total_y-2)),color="grey", width=1)
rod2 = c.draw_line((WIDTH/4*2,0), (WIDTH/4*2,HEIGHT/total_y*(total_y-2)),color="grey", width=1)
rod3 = c.draw_line((WIDTH/4*3,0), (WIDTH/4*3,HEIGHT/total_y*(total_y-2)),color="grey", width=1)
t1 = c.draw_text("1", (WIDTH/4,HEIGHT/total_y*(total_y-1)), font=("Arial",20),color="red")
t2 = c.draw_text("2", (WIDTH/4*2,HEIGHT/total_y*(total_y-1)), font=("Arial",20),color="red")
t3 = c.draw_text("3", (WIDTH/4*3,HEIGHT/total_y*(total_y-1)), font=("Arial",20),color="red")
class Game:
stacks = [[],[],[]]
log = []
turn = 0
class Disk:
def __init__(self, rank, color="yellow" ):
self.rank = rank
self.color = color
w = 1 / NUMBER_OF_DISCS
self.width = w * self.rank * WIDTH /4
self.height = HEIGHT /(NUMBER_OF_DISCS + 3)
self.figure = None # figure id for canvas drawing
def redraw(self, canvas):
# figure out self.x, self.y by iterating over all stacks
for rod_number, stack in enumerate(Game.stacks, 1):
for disk_number, disk in enumerate(stack,1):
disk.x = rod_number
disk.y = disk_number
if self.figure is not None:
canvas.delete_figure(self.figure)
self.figure = canvas.draw_line((self.x * WIDTH/4 - self.width/2, self.height * self.y - self.height/2 ),
(self.x * WIDTH/4 + self.width/2, self.height * self.y- self.height/2),
color = self.color,
width = self.height)
def __repr__(self):
# representation
return f"<-{self.rank}->"
# def __lt__(self, other):
# # lesser than, used for sorting
# # returns True if self is lesser than other
# return self.rank < other.rank
def draw_all_disks():
for stack in Game.stacks:
for d in stack:
d.redraw(c)
def is_move_legal(from_stack, to_stack):
"""returns boolean and error message"""
if len(Game.stacks[from_stack-1]) == 0:
return False, "Error: from_stack is empty"
mover = Game.stacks[from_stack-1][-1] # top disk
if len(Game.stacks[to_stack-1]) > 0:
if mover.rank > Game.stacks[to_stack-1][-1].rank:
#print("Error: a too small disk is on top of to_stack")
return False, "Error: a too small disk is on top of to_stack"
return True, ""
def move_disk(from_stack, to_stack, silent=False):
"""move a disk from one stack to another.
returns True if sucessfull, otherwise False"""
legal = is_move_legal(from_stack, to_stack)
if not legal[0]:
if not silent:
sg.PopupError(legal[1])
return False
# move the disk
mover = Game.stacks[from_stack-1].pop(-1)
Game.stacks[to_stack-1].append(mover)
Game.turn += 1
Game.log.append(f"Turn # {Game.turn}: moving disc # {mover.rank} from stack {from_stack} to stack {to_stack}.")
def solve(n, from_stack, to_stack, help_stack):
"""recursive function to solve tower of hanoi"""
if n == 0:
return
solve(n-1, from_stack, help_stack, to_stack)
move_disk(from_stack, to_stack, silent=True)
draw_all_disks()
window2.finalize()
time.sleep(PAUSE)
solve(n-1,help_stack, to_stack, from_stack)
def reset_discs():
Game.stacks = [[],[],[]]
# create colorful first stack of discs
for i in range(NUMBER_OF_DISCS):
Game.stacks[0].append(Disk(NUMBER_OF_DISCS- (i),COLORS[i]))
def delete_all_discs():
for s in Game.stacks:
for d in s:
c.delete_figure(d.figure)
reset_discs()
draw_all_disks()
while True:
event, values = window2.read()
if event in("Exit", sg.WIN_CLOSED):
break
if event == "Log":
sg.PopupScrolled("\n".join(Game.log))
if event == "Reset":
delete_all_discs()
reset_discs()
draw_all_disks()
if (">>" in event) or ("<<" in event):
#print("before:",Game.stacks)
if event == "1>>2":
move_disk(1,2)
if event == "1>>3":
move_disk(1,3)
if event == "1<<2":
move_disk(2,1)
if event == "2>>3":
move_disk(2,3)
if event == "1<<3":
move_disk(3,1)
if event == "2<<3":
move_disk(3,2)
draw_all_disks()
elif event == "Solve":
solve(NUMBER_OF_DISCS,1,3,2)
window2.close()
print("bye-bye")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment