"""
Discovering Computer Science, Third Edition
Jessen Havill

Inspired by the minimal_hanoi example in turtledemo.
"""

import turtle

DISK_WIDTH = 33
TOWER_SPACING = 250
NUM_DISKS = 8

def setup_towers(n: int) -> list[list[turtle.Turtle]]:
    """Draw the three pegs and create n disks on the left one.
       Each disk is a Turtle object.
    """
    
    posts = turtle.Turtle()
    posts.hideturtle()
    posts.width(15)
    posts.color('brown')
    posts.left(90)
    for tower_num in range(3):
        posts.up()
        posts.goto(-TOWER_SPACING + tower_num * TOWER_SPACING, 0)
        posts.down()
        posts.forward(NUM_DISKS * (DISK_WIDTH))
    
    towers = [[], [], []]
    for disk_num in range(n, 0, -1):
        disk = turtle.Turtle()
        disk.hideturtle()
        disk.up()
        disk.goto(-TOWER_SPACING, DISK_WIDTH * (n - disk_num))
        disk.shape('square')
        disk.shapesize(1.5, 1.5 * disk_num, 2)
        disk.fillcolor(disk_num / n, 0, 1 - disk_num / n)  # fancy colors
        disk.showturtle()
        towers[0].append(disk)
    return towers
        
def move(source: int, dest: int, towers: list[list[turtle.Turtle]]) -> None:
    """Animate moving the top disk on the source peg to the dest peg."""
    
    disk = towers[source].pop()  # remove top disk from the source peg
    
    disk.sety(NUM_DISKS * (DISK_WIDTH + 4))            # move disk up
    disk.setx(-TOWER_SPACING + dest * TOWER_SPACING)   # move disk over
    disk.sety(DISK_WIDTH * len(towers[dest]))          # move disk down
    
    towers[dest].append(disk)   # push disk onto the dest peg

def hanoi(n: int, source: int, dest: int, extra: int, towers: list[list[turtle.Turtle]]) -> None:
    """Animate the solution for the Tower of Hanoi puzzle.
    
    Parameters:
        n:      the number of disks
        source: the source peg
        dest:   the destination peg
        extra:  the other peg
        towers: a list of three lists containing disks on each of the pegs;
                each disk is a Turtle object
        
    Return value: None
    """

    if n > 0:
        hanoi(n-1, source, extra, dest, towers)
        move(source, dest, towers)
        hanoi(n-1, extra, dest, source, towers)

def main():
    towers = setup_towers(NUM_DISKS)
    hanoi(NUM_DISKS, 0, 2, 1, towers)
    
    turtle.done()

main()
