17db96d56Sopenharmony_ci#!/usr/bin/env python3 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ci""" 47db96d56Sopenharmony_ciAnimated Towers of Hanoi using Tk with optional bitmap file in background. 57db96d56Sopenharmony_ci 67db96d56Sopenharmony_ciUsage: hanoi.py [n [bitmapfile]] 77db96d56Sopenharmony_ci 87db96d56Sopenharmony_cin is the number of pieces to animate; default is 4, maximum 15. 97db96d56Sopenharmony_ci 107db96d56Sopenharmony_ciThe bitmap file can be any X11 bitmap file (look in /usr/include/X11/bitmaps for 117db96d56Sopenharmony_cisamples); it is displayed as the background of the animation. Default is no 127db96d56Sopenharmony_cibitmap. 137db96d56Sopenharmony_ci""" 147db96d56Sopenharmony_ci 157db96d56Sopenharmony_cifrom tkinter import Tk, Canvas 167db96d56Sopenharmony_ci 177db96d56Sopenharmony_ci# Basic Towers-of-Hanoi algorithm: move n pieces from a to b, using c 187db96d56Sopenharmony_ci# as temporary. For each move, call report() 197db96d56Sopenharmony_cidef hanoi(n, a, b, c, report): 207db96d56Sopenharmony_ci if n <= 0: return 217db96d56Sopenharmony_ci hanoi(n-1, a, c, b, report) 227db96d56Sopenharmony_ci report(n, a, b) 237db96d56Sopenharmony_ci hanoi(n-1, c, b, a, report) 247db96d56Sopenharmony_ci 257db96d56Sopenharmony_ci 267db96d56Sopenharmony_ci# The graphical interface 277db96d56Sopenharmony_ciclass Tkhanoi: 287db96d56Sopenharmony_ci 297db96d56Sopenharmony_ci # Create our objects 307db96d56Sopenharmony_ci def __init__(self, n, bitmap=None): 317db96d56Sopenharmony_ci self.n = n 327db96d56Sopenharmony_ci self.tk = tk = Tk() 337db96d56Sopenharmony_ci self.canvas = c = Canvas(tk) 347db96d56Sopenharmony_ci c.pack() 357db96d56Sopenharmony_ci width, height = tk.getint(c['width']), tk.getint(c['height']) 367db96d56Sopenharmony_ci 377db96d56Sopenharmony_ci # Add background bitmap 387db96d56Sopenharmony_ci if bitmap: 397db96d56Sopenharmony_ci self.bitmap = c.create_bitmap(width//2, height//2, 407db96d56Sopenharmony_ci bitmap=bitmap, 417db96d56Sopenharmony_ci foreground='blue') 427db96d56Sopenharmony_ci 437db96d56Sopenharmony_ci # Generate pegs 447db96d56Sopenharmony_ci pegwidth = 10 457db96d56Sopenharmony_ci pegheight = height//2 467db96d56Sopenharmony_ci pegdist = width//3 477db96d56Sopenharmony_ci x1, y1 = (pegdist-pegwidth)//2, height*1//3 487db96d56Sopenharmony_ci x2, y2 = x1+pegwidth, y1+pegheight 497db96d56Sopenharmony_ci self.pegs = [] 507db96d56Sopenharmony_ci p = c.create_rectangle(x1, y1, x2, y2, fill='black') 517db96d56Sopenharmony_ci self.pegs.append(p) 527db96d56Sopenharmony_ci x1, x2 = x1+pegdist, x2+pegdist 537db96d56Sopenharmony_ci p = c.create_rectangle(x1, y1, x2, y2, fill='black') 547db96d56Sopenharmony_ci self.pegs.append(p) 557db96d56Sopenharmony_ci x1, x2 = x1+pegdist, x2+pegdist 567db96d56Sopenharmony_ci p = c.create_rectangle(x1, y1, x2, y2, fill='black') 577db96d56Sopenharmony_ci self.pegs.append(p) 587db96d56Sopenharmony_ci self.tk.update() 597db96d56Sopenharmony_ci 607db96d56Sopenharmony_ci # Generate pieces 617db96d56Sopenharmony_ci pieceheight = pegheight//16 627db96d56Sopenharmony_ci maxpiecewidth = pegdist*2//3 637db96d56Sopenharmony_ci minpiecewidth = 2*pegwidth 647db96d56Sopenharmony_ci self.pegstate = [[], [], []] 657db96d56Sopenharmony_ci self.pieces = {} 667db96d56Sopenharmony_ci x1, y1 = (pegdist-maxpiecewidth)//2, y2-pieceheight-2 677db96d56Sopenharmony_ci x2, y2 = x1+maxpiecewidth, y1+pieceheight 687db96d56Sopenharmony_ci dx = (maxpiecewidth-minpiecewidth) // (2*max(1, n-1)) 697db96d56Sopenharmony_ci for i in range(n, 0, -1): 707db96d56Sopenharmony_ci p = c.create_rectangle(x1, y1, x2, y2, fill='red') 717db96d56Sopenharmony_ci self.pieces[i] = p 727db96d56Sopenharmony_ci self.pegstate[0].append(i) 737db96d56Sopenharmony_ci x1, x2 = x1 + dx, x2-dx 747db96d56Sopenharmony_ci y1, y2 = y1 - pieceheight-2, y2-pieceheight-2 757db96d56Sopenharmony_ci self.tk.update() 767db96d56Sopenharmony_ci self.tk.after(25) 777db96d56Sopenharmony_ci 787db96d56Sopenharmony_ci # Run -- never returns 797db96d56Sopenharmony_ci def run(self): 807db96d56Sopenharmony_ci while True: 817db96d56Sopenharmony_ci hanoi(self.n, 0, 1, 2, self.report) 827db96d56Sopenharmony_ci hanoi(self.n, 1, 2, 0, self.report) 837db96d56Sopenharmony_ci hanoi(self.n, 2, 0, 1, self.report) 847db96d56Sopenharmony_ci hanoi(self.n, 0, 2, 1, self.report) 857db96d56Sopenharmony_ci hanoi(self.n, 2, 1, 0, self.report) 867db96d56Sopenharmony_ci hanoi(self.n, 1, 0, 2, self.report) 877db96d56Sopenharmony_ci 887db96d56Sopenharmony_ci # Reporting callback for the actual hanoi function 897db96d56Sopenharmony_ci def report(self, i, a, b): 907db96d56Sopenharmony_ci if self.pegstate[a][-1] != i: raise RuntimeError # Assertion 917db96d56Sopenharmony_ci del self.pegstate[a][-1] 927db96d56Sopenharmony_ci p = self.pieces[i] 937db96d56Sopenharmony_ci c = self.canvas 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_ci # Lift the piece above peg a 967db96d56Sopenharmony_ci ax1, ay1, ax2, ay2 = c.bbox(self.pegs[a]) 977db96d56Sopenharmony_ci while True: 987db96d56Sopenharmony_ci x1, y1, x2, y2 = c.bbox(p) 997db96d56Sopenharmony_ci if y2 < ay1: break 1007db96d56Sopenharmony_ci c.move(p, 0, -1) 1017db96d56Sopenharmony_ci self.tk.update() 1027db96d56Sopenharmony_ci 1037db96d56Sopenharmony_ci # Move it towards peg b 1047db96d56Sopenharmony_ci bx1, by1, bx2, by2 = c.bbox(self.pegs[b]) 1057db96d56Sopenharmony_ci newcenter = (bx1+bx2)//2 1067db96d56Sopenharmony_ci while True: 1077db96d56Sopenharmony_ci x1, y1, x2, y2 = c.bbox(p) 1087db96d56Sopenharmony_ci center = (x1+x2)//2 1097db96d56Sopenharmony_ci if center == newcenter: break 1107db96d56Sopenharmony_ci if center > newcenter: c.move(p, -1, 0) 1117db96d56Sopenharmony_ci else: c.move(p, 1, 0) 1127db96d56Sopenharmony_ci self.tk.update() 1137db96d56Sopenharmony_ci 1147db96d56Sopenharmony_ci # Move it down on top of the previous piece 1157db96d56Sopenharmony_ci pieceheight = y2-y1 1167db96d56Sopenharmony_ci newbottom = by2 - pieceheight*len(self.pegstate[b]) - 2 1177db96d56Sopenharmony_ci while True: 1187db96d56Sopenharmony_ci x1, y1, x2, y2 = c.bbox(p) 1197db96d56Sopenharmony_ci if y2 >= newbottom: break 1207db96d56Sopenharmony_ci c.move(p, 0, 1) 1217db96d56Sopenharmony_ci self.tk.update() 1227db96d56Sopenharmony_ci 1237db96d56Sopenharmony_ci # Update peg state 1247db96d56Sopenharmony_ci self.pegstate[b].append(i) 1257db96d56Sopenharmony_ci 1267db96d56Sopenharmony_ci 1277db96d56Sopenharmony_cidef main(): 1287db96d56Sopenharmony_ci import sys 1297db96d56Sopenharmony_ci 1307db96d56Sopenharmony_ci # First argument is number of pegs, default 4 1317db96d56Sopenharmony_ci if sys.argv[1:]: 1327db96d56Sopenharmony_ci n = int(sys.argv[1]) 1337db96d56Sopenharmony_ci else: 1347db96d56Sopenharmony_ci n = 4 1357db96d56Sopenharmony_ci 1367db96d56Sopenharmony_ci # Second argument is bitmap file, default none 1377db96d56Sopenharmony_ci if sys.argv[2:]: 1387db96d56Sopenharmony_ci bitmap = sys.argv[2] 1397db96d56Sopenharmony_ci # Reverse meaning of leading '@' compared to Tk 1407db96d56Sopenharmony_ci if bitmap[0] == '@': bitmap = bitmap[1:] 1417db96d56Sopenharmony_ci else: bitmap = '@' + bitmap 1427db96d56Sopenharmony_ci else: 1437db96d56Sopenharmony_ci bitmap = None 1447db96d56Sopenharmony_ci 1457db96d56Sopenharmony_ci # Create the graphical objects... 1467db96d56Sopenharmony_ci h = Tkhanoi(n, bitmap) 1477db96d56Sopenharmony_ci 1487db96d56Sopenharmony_ci # ...and run! 1497db96d56Sopenharmony_ci h.run() 1507db96d56Sopenharmony_ci 1517db96d56Sopenharmony_ci 1527db96d56Sopenharmony_ci# Call main when run as script 1537db96d56Sopenharmony_ciif __name__ == '__main__': 1547db96d56Sopenharmony_ci main() 155