Created
June 11, 2022 09:10
-
-
Save horstjens/49cc1bf6117d9d03788dd3826727eb48 to your computer and use it in GitHub Desktop.
tower_of_hanoi
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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