2
\$\begingroup\$

I wrote a program to show graphically the solution of the problem of the Tower of Hanoi. Here is a video of an example run.

The logic of the solving is taken from StackOverflow (see links in docstring), I wrote the part dealing with the graphics, so review is most welcome on the graphical part that I wrote.

The code is pretty simple, tested and documented, so no further introduction is needed.

# -*- coding: utf-8 -*-
import doctest
import time
import pygame

SPACE_PER_PEG = 200

def hanoi(pegs, start, target, n):
    """
    From stackoverflow:
      http://stackoverflow.com/questions/23107610/towers-of-hanoi-python-understanding-recursion
      http://stackoverflow.com/questions/41418275/python-translating-a-printing-recursive-function-into-a-generator

    This function, given a starting position of an hanoi tower, yields
    the sequence of optimal steps that leads to the solution.

    >>> for position in hanoi([ [3, 2, 1], [], [] ], 0, 2, 3): print position
    [[3, 2], [], [1]]
    [[3], [2], [1]]
    [[3], [2, 1], []]
    [[], [2, 1], [3]]
    [[1], [2], [3]]
    [[1], [], [3, 2]]
    [[], [], [3, 2, 1]]

    """
    assert len(pegs[start]) >= n, 'not enough disks on peg'
    if n == 1:
        pegs[target].append(pegs[start].pop())
        yield pegs
    else:
        aux = 3 - start - target  # start + target + aux = 3
        for i in hanoi(pegs, start, aux, n-1): yield i
        for i in hanoi(pegs, start, target, 1): yield i
        for i in hanoi(pegs, aux, target, n-1): yield i

def display_pile_of_pegs(pegs, start_x, start_y, peg_height, screen):
    """
    Given a pile of pegs, displays them on the screen, nicely inpilated
    like in a piramid, the smaller in lighter color.
    """
    for i, pegwidth in enumerate(pegs):

        pygame.draw.rect(
            screen,
            # Smaller pegs are ligher in color
            (255-pegwidth, 255-pegwidth, 255-pegwidth),
            (
              start_x + (SPACE_PER_PEG - pegwidth)/2 , # Handles alignment putting pegs in the middle, like a piramid
              start_y - peg_height * i,         # Pegs are one on top of the other, height depends on iteration
              pegwidth,
              peg_height
            )
        )

def visual_hanoi_simulation(number_of_pegs, base_width, peg_height, sleeping_interval):
    """
    Visually shows the process of optimal solution of an hanoi tower problem.
    """
    pegs = [[i * base_width for i in reversed(range(1, number_of_pegs+1))], [], []]
    positions = hanoi(pegs, 0, 2, number_of_pegs)

    pygame.init()
    screen = pygame.display.set_mode( (650, 650) )
    pygame.display.set_caption('Towers of Hanoi')

    for position in positions:
        screen.fill((255, 255, 255)) 
        for i, pile in enumerate(position):
            display_pile_of_pegs(pile, 50 + SPACE_PER_PEG*i, 500, peg_height, screen)
        pygame.display.update()
        time.sleep(sleeping_interval)

    pygame.quit()

if __name__ == "__main__":
    doctest.testmod()
    visual_hanoi_simulation(
        number_of_pegs = 4,
        base_width = 30,
        peg_height = 40,
        sleeping_interval = 0.5
    )
    visual_hanoi_simulation(
        number_of_pegs = 8,
        base_width = 20,
        peg_height = 30,
        sleeping_interval = 0.2
    )
\$\endgroup\$
3
\$\begingroup\$

Very nice code!

Herea are a few small things:

  1. Since your function hanoi is a generator, you don't need to save it to a variable, you can use it directly in the for loop:
for position in hanoi(pegs, 0, 2, number_of_pegs):
  1. According to PEP8:

Compound statements (multiple statements on the same line) are generally discouraged.

  1. reversed(range(1, number_of_pegs+1)) is the same as range(number_of_pegs, 0, -1)

  2. You calculate 255-pegwidth three times in a row, just use (255 - pegwidth,)*3 and use that.

\$\endgroup\$
  • 1
    \$\begingroup\$ Nice comments. For what it is worth, I'd rather read code using reversed than -1 as a step in range. In any case, [i+1 for i in reversed(range(nb))] would look even better to me. \$\endgroup\$ – Josay Jan 2 '17 at 17:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.