I'm trying to figure out how to implement a non-recursive algorithm for the Hanoi Towers problem in function hanoi_2
below, but I have no idea how to continue...
It throws an error: "can't pop from empty list". It works somehow when I input an odd number, however, when the third turn passes things go wrong. When an even number is input as the number of discs the program doesn't even start.
What is wrong?
from turtle import *
from tkinter import * # used for the dialog box
from tkinter.simpledialog import askinteger # used for the dialog box
from tkinter import ttk # used for the progress bar
import time # used for time-related functions (in pause)
import pickle # used to save an object to a file
class Disc(Turtle):
def __init__(self, n):
Turtle.__init__(self, shape="square", visible=False)
self.pu()
self.shapesize(1.5, n*1.5, 2) # square-->rectangle
self.fillcolor(n/10., 0, 1-n/10.)
self.st()
self.speed(11-n) # sets the speed of movement of the rectangles (the bigger, the slower)
self.moves = 0 # stores the number of times the disc is moved
class Tower(list):
"""Hanoi tower, a subclass of built-in type list"""
def __init__(self, x):
"""create an empty tower. x is x-position of peg"""
self.x = x
def push(self, d):
d.setx(self.x)
d.sety(-150+34*len(self))
d.clear()
d.write("Moved %s times" %(str(d.moves)), align="left", font=("Courier", 16, "bold"))
d.moves += 1 # increments the number of moves each time the disc is moved
self.append(d)
def pop(self):
d = list.pop(self)
d.sety(150)
return d
def hanoi(n, from_, with_, to_):
global moves
global ans
clear()
if n > 0:
hanoi(n-1, from_, to_, with_)
moves += 1 # amount of total moves is incremented
to_.push(from_.pop())
hanoi(n-1, with_, from_, to_)
sety(-255)
write("Total moves: %s" % (moves), align="center", font=("Courier", 16, "bold"))
sety(-320)
progress_bar(ans) # calls progress bar function
def hanoi_2(n, A, B, C):
global moves
clear()
if n%2==0:
B=C
C=A
A=B
for i in range(1,(2**n)-1):
if i%3==1:
C.push(A.pop())
if i%3==2:
B.push(A.pop())
if i%3==0:
B.push(C.pop())
The issue with the even number of discs is that the stack swap is not right: you seem to want to cycle the three stacks (which you do in a wrong way as you lose the reference to the original B list), while actually you need to swap only the B and C stacks, which you can do as follows:
B, C = C, B
The issue with the algorithm is that although you have the involved two stacks right (based on i%3
), you must still determine which of the two involved stacks is the giver and which one the taker, as this is not always the same! It is probably best if you write a function for this that takes two stacks and determines which of the two possible "directions" is a valid one and then performs that move:
def validMove(A, B):
if not len(A):
A.push(B.pop())
elif not len(B):
B.push(A.pop())
elif A[-1] > B[-1]:
A.push(B.pop())
else:
B.push(A.pop())
Now the main algorithm will look like this:
for i in range(1,2**n):
if i%3==1:
validMove(A, C)
if i%3==2:
validMove(A, B)
if i%3==0:
validMove(B, C)