Skip to content

Latest commit

 

History

History
182 lines (135 loc) · 6.58 KB

File metadata and controls

182 lines (135 loc) · 6.58 KB

七十七、汉诺塔

原文:http://inventwithpython.com/bigbookpython/project77.html

汉诺塔是一款移动堆叠的益智游戏,有三根柱子,你可以在上面堆叠不同大小的圆盘。游戏的目标是将一个塔盘移到另一个柱子上。但是,一次只能移动一个磁盘,较大的磁盘不能放在较小的磁盘上。找出某种模式将有助于你解决这个难题。你能发现它吗?(提示:尝试将TOTAL_DISKS变量设置为34,以先解决一个更简单的版本。)

运行示例

当您运行towerofhanoi.py时,输出将如下所示:

The Tower of Hanoi, by Al Sweigart email@protected

Move the tower of disks, one disk at a time, to another tower. Larger
disks cannot rest on top of a smaller disk.

More info at https://en.wikipedia.org/wiki/Tower_of_Hanoi

     ||          ||          ||
    @email@protected         ||          ||
   @@email@protected@        ||          ||
  @@@email@protected@@       ||          ||
 @@@@email@protected@@@      ||          ||
@@@@@email@protected@@@@     ||          ||
      A           B           C

Enter the letters of "from" and "to" towers, or QUIT.
(e.g. AB to moves a disk from tower A to tower B.)
> ab
     ||          ||          ||
     ||          ||          ||
   @@email@protected@        ||          ||
  @@@email@protected@@       ||          ||
 @@@@email@protected@@@      ||          ||
@@@@@email@protected@@@@    @email@protected         ||
      A           B           C

Enter the letters of "from" and "to" towers, or QUIT.
(e.g. AB to moves a disk from tower A to tower B.)
`--snip--`

工作原理

表示塔的数据结构是一个整数列表。每个整数都是磁盘的大小。列表中的第一个整数代表底部磁盘,最后一个整数代表顶部磁盘。例如,[5, 4, 2]将代表以下塔:

 ||
     ||
   @@email@protected@
 @@@@email@protected@@@
@@@@@email@protected@@@@

Python 的append()pop()列表方法可以分别在列表末尾添加和删除值。正如someList[0]someList[1]允许我们访问列表中的第一个和第二个值一样,Python 允许我们使用负索引来访问列表末尾的值,使用像someList[-1]someList[-2]这样的表达式,它们分别访问列表中的最后一个和倒数第二个值。这对于查找当前位于塔顶的磁盘非常有用。

"""The Tower of Hanoi, by Al Sweigart email@protected
A stack-moving puzzle game.
This code is available at https://nostarch.com/big-book-small-python-programming
Tags: short, game, puzzle"""

import copy
import sys

TOTAL_DISKS = 5  # More disks means a more difficult puzzle.

# Start with all disks on tower A:
COMPLETE_TOWER = list(range(TOTAL_DISKS, 0, -1))


def main():
   print("""The Tower of Hanoi, by Al Sweigart email@protected

Move the tower of disks, one disk at a time, to another tower. Larger
disks cannot rest on top of a smaller disk.

More info at https://en.wikipedia.org/wiki/Tower_of_Hanoi
"""
   )

   # Set up the towers. The end of the list is the top of the tower.
   towers = {'A': copy.copy(COMPLETE_TOWER), 'B': [], 'C': []}

   while True:  # Run a single turn.
       # Display the towers and disks:
       displayTowers(towers)

       # Ask the user for a move:
       fromTower, toTower = askForPlayerMove(towers)

       # Move the top disk from fromTower to toTower:
       disk = towers[fromTower].pop()
       towers[toTower].append(disk)

       # Check if the user has solved the puzzle:
       if COMPLETE_TOWER in (towers['B'], towers['C']):
           displayTowers(towers)  # Display the towers one last time.
           print('You have solved the puzzle! Well done!')
           sys.exit()


def askForPlayerMove(towers):
   """Asks the player for a move. Returns (fromTower, toTower)."""

   while True:  # Keep asking player until they enter a valid move.
       print('Enter the letters of "from" and "to" towers, or QUIT.')
       print('(e.g. AB to moves a disk from tower A to tower B.)')
       response = input('> ').upper().strip()

       if response == 'QUIT':
           print('Thanks for playing!')
           sys.exit()

       # Make sure the user entered valid tower letters:
       if response not in ('AB', 'AC', 'BA', 'BC', 'CA', 'CB'):
           print('Enter one of AB, AC, BA, BC, CA, or CB.')
           continue  # Ask player again for their move.

       # Syntactic sugar - Use more descriptive variable names:
       fromTower, toTower = response[0], response[1]

       if len(towers[fromTower]) == 0:
           # The "from" tower cannot be an empty tower:
           print('You selected a tower with no disks.')
           continue  # Ask player again for their move.
       elif len(towers[toTower]) == 0:
           # Any disk can be moved onto an empty "to" tower:
           return fromTower, toTower
       elif towers[toTower][-1] < towers[fromTower][-1]:
           print('Can\'t put larger disks on top of smaller ones.')
           continue  # Ask player again for their move.
       else:
           # This is a valid move, so return the selected towers:
           return fromTower, toTower


def displayTowers(towers):
   """Display the current state."""

   # Display the three towers:
   for level in range(TOTAL_DISKS, -1, -1):
       for tower in (towers['A'], towers['B'], towers['C']):
           if level >= len(tower):
               displayDisk(0)  # Display the bare pole with no disk.
           else:
               displayDisk(tower[level])  # Display the disk.
       print()

   # Display the tower labels A, B, and C.
   emptySpace = ' ' * (TOTAL_DISKS)
   print('{0} A{0}{0} B{0}{0} C\n'.format(emptySpace))


def displayDisk(width):
   """Display a disk of the given width. A width of 0 means no disk."""
    emptySpace = ' ' * (TOTAL_DISKS - width)

    if width == 0:
        # Display a pole segment without a disk:
        print(emptySpace + '||' + emptySpace, end='')
    else:
        # Display the disk:
        disk = '@' * width
        numLabel = str(width).rjust(2, '_')
        print(emptySpace + disk + numLabel + disk + emptySpace, end='')


# If the program is run (instead of imported), run the game:
if __name__ == '__main__':
    main() 

探索程序

试着找出下列问题的答案。尝试对代码进行一些修改,然后重新运行程序,看看这些修改有什么影响。

  1. 如果删除或注释掉第 73、74 和 75 行会发生什么?
  2. 如果把第 100 行的emptySpace = ' ' * (TOTAL_DISKS - width)改成emptySpace = ' '会怎么样?
  3. 如果把 102 行的width == 0改成width != 0会怎么样?