Refactorization
This commit is contained in:
parent
05fdb91364
commit
16f966086b
103
animation.py
103
animation.py
|
@ -2,26 +2,10 @@ import matplotlib.pyplot as plt
|
||||||
import matplotlib.animation as animation
|
import matplotlib.animation as animation
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import copy
|
import copy
|
||||||
|
import argparse
|
||||||
TOWER_NAMES = ['A', 'B', 'C']
|
|
||||||
|
|
||||||
|
|
||||||
def move_and_save(src, dest, towers, states):
|
def animate_hanoi(n, frame_delay=750, src=0, dest=1, aux=2):
|
||||||
disk = towers[src].pop()
|
|
||||||
towers[dest].append(disk)
|
|
||||||
states.append(((disk, src, dest), copy.deepcopy(towers)))
|
|
||||||
|
|
||||||
|
|
||||||
def solve_game(n, src, dest, aux, towers, states):
|
|
||||||
if n == 1:
|
|
||||||
move_and_save(src, dest, towers, states)
|
|
||||||
else:
|
|
||||||
solve_game(n - 1, src, aux, dest, towers, states)
|
|
||||||
move_and_save(src, dest, towers, states)
|
|
||||||
solve_game(n - 1, aux, dest, src, towers, states)
|
|
||||||
|
|
||||||
|
|
||||||
def animate_hanoi(n, frame_delay=0.5, src=0, dest=1, aux=2):
|
|
||||||
colors = []
|
colors = []
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
# Calculate the hue value
|
# Calculate the hue value
|
||||||
|
@ -30,52 +14,71 @@ def animate_hanoi(n, frame_delay=0.5, src=0, dest=1, aux=2):
|
||||||
rgb = plt.cm.hsv(hue)[:3] # Use HSV colormap and take RGB values
|
rgb = plt.cm.hsv(hue)[:3] # Use HSV colormap and take RGB values
|
||||||
colors.append(rgb)
|
colors.append(rgb)
|
||||||
|
|
||||||
# Create the towers
|
|
||||||
towers = [[], [], []]
|
|
||||||
for i in range(n):
|
|
||||||
towers[src].append(n - i)
|
|
||||||
# Solve it
|
|
||||||
states = [(None, copy.deepcopy(towers))]
|
|
||||||
solve_game(n, src, dest, aux, towers, states)
|
|
||||||
|
|
||||||
fig, ax = plt.subplots()
|
fig, ax = plt.subplots()
|
||||||
fig.suptitle(f"Towers of Hanoi simulation for n={n}", fontsize=21)
|
fig.suptitle(f"Towers of Hanoi simulation for n={n}", fontsize=21)
|
||||||
ax.set_xlim(-1, 3)
|
ax.set_xlim(-1, 3)
|
||||||
ax.set_ylim(0, n + 1)
|
ax.set_ylim(0, n + 1)
|
||||||
ax.set_xticks([0, 1, 2])
|
ax.set_xticks([0, 1, 2])
|
||||||
ax.set_xticklabels(TOWER_NAMES)
|
ax.set_xticklabels(['A', 'B', 'C'])
|
||||||
ax.set_yticks([])
|
ax.set_yticks([])
|
||||||
|
|
||||||
plot_elements = []
|
frames = []
|
||||||
|
|
||||||
def update(frame):
|
def move_and_frame(src, dest):
|
||||||
move, towers = states[frame]
|
disk = towers[src].pop()
|
||||||
|
towers[dest].append(disk)
|
||||||
# Clear previous plot
|
|
||||||
for el in plot_elements:
|
|
||||||
el.remove()
|
|
||||||
plot_elements.clear()
|
|
||||||
|
|
||||||
# Draw the disks
|
# Draw the disks
|
||||||
|
frame = []
|
||||||
for i, tower in enumerate(towers):
|
for i, tower in enumerate(towers):
|
||||||
for j, disk in enumerate(tower):
|
for j, disk in enumerate(tower):
|
||||||
plot_elements.append(ax.bar(i, 1, bottom=j,
|
frame += ax.bar(i, 1, bottom=j,
|
||||||
width=0.5, color=colors[disk-1]))
|
width=0.5, color=colors[disk-1]).patches
|
||||||
plot_elements.append(ax.text(i, j + 0.5, str(disk), ha='center',
|
frame.append(ax.text(i, j + 0.5, str(disk), ha='center',
|
||||||
va='center', color='white', fontsize=16))
|
va='center', color='white', fontsize=16))
|
||||||
|
|
||||||
sleep(frame_delay) # Delay the frame
|
frames.append(frame)
|
||||||
if move:
|
|
||||||
print(
|
|
||||||
f"🟦 Move {frame}: Disk ({move[0]}) on tower {TOWER_NAMES[move[1]]} to tower {TOWER_NAMES[move[2]]}")
|
|
||||||
return plot_elements
|
|
||||||
|
|
||||||
ani = animation.FuncAnimation(
|
def solve_game(n, src, dest, aux):
|
||||||
fig, update, frames=len(states), repeat=False)
|
if n == 1:
|
||||||
plt.show()
|
move_and_frame(src, dest)
|
||||||
|
else:
|
||||||
|
solve_game(n - 1, src, aux, dest)
|
||||||
|
move_and_frame(src, dest)
|
||||||
|
solve_game(n - 1, aux, dest, src)
|
||||||
|
|
||||||
|
# Create the towers
|
||||||
|
towers = [[], [], []]
|
||||||
|
for i in range(n):
|
||||||
|
towers[src].append(n - i)
|
||||||
|
|
||||||
|
# Frame the initial state
|
||||||
|
move_and_frame(0, 0)
|
||||||
|
|
||||||
|
# Solve it
|
||||||
|
solve_game(n, src, dest, aux)
|
||||||
|
|
||||||
|
ani = animation.ArtistAnimation(fig, frames, interval=frame_delay, blit=True,
|
||||||
|
repeat=False)
|
||||||
|
|
||||||
|
return ani
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
n = int(input("❔ Number of disks in the game: "))
|
parser = argparse.ArgumentParser(
|
||||||
d = int(input("❔ Delay between frames (in seconds): "))
|
description="A simple program to animate the optimal solution for a Towers of Hanoi game with n disks")
|
||||||
animate_hanoi(n, d)
|
parser.add_argument("--disk-number", "-n", type=int,
|
||||||
|
help="Number of disks to simulate. Asks by default", default=0)
|
||||||
|
parser.add_argument("--interval", "-i", type=int,
|
||||||
|
help="Interval (in ms) between frames", default=750)
|
||||||
|
parser.add_argument("--output", "-o", type=str,
|
||||||
|
help="File to write the animation to (if specified)", default=None)
|
||||||
|
args = parser.parse_args()
|
||||||
|
if args.disk_number == 0:
|
||||||
|
args.disk_number = int(input("❔ Number of disks in the game: "))
|
||||||
|
ani = animate_hanoi(args.disk_number, args.interval)
|
||||||
|
if args.output:
|
||||||
|
ani.save(args.output)
|
||||||
|
print("Animation saved succesfully")
|
||||||
|
else:
|
||||||
|
plt.show()
|
||||||
|
|
Loading…
Reference in a new issue