汉诺塔是一款移动堆叠的益智游戏,有三根柱子,你可以在上面堆叠不同大小的圆盘。游戏的目标是将一个塔盘移到另一个柱子上。但是,一次只能移动一个磁盘,较大的磁盘不能放在较小的磁盘上。找出某种模式将有助于你解决这个难题。你能发现它吗?(提示:尝试将TOTAL_DISKS变量设置为3或4,以先解决一个更简单的版本。)
当您运行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() 试着找出下列问题的答案。尝试对代码进行一些修改,然后重新运行程序,看看这些修改有什么影响。
- 如果删除或注释掉第 73、74 和 75 行会发生什么?
- 如果把第 100 行的
emptySpace = ' ' * (TOTAL_DISKS - width)改成emptySpace = ' '会怎么样? - 如果把 102 行的
width == 0改成width != 0会怎么样?
