In this solitaire puzzle game, you must use three buckets (three-liter, five-liter, and eight-liter buckets) to collect exactly four liters of water in one of the buckets. Buckets can only be emptied, completely filled, or poured into another bucket. For example, you can fill the five-liter bucket and then pour its contents into the three-liter bucket, leaving you with a full three-liter bucket and two liters of water in the five-liter bucket.
With some effort, you should be able to solve the puzzle. But can you figure out how to solve it with the minimal number of moves?
When you run waterbucket.py, the output will look like this:
Water Bucket Puzzle, by Al Sweigart [email protected]
Try to get 4L of water into one of these
buckets:
8| |
7| |
6| |
5| | 5| |
4| | 4| |
3| | 3| | 3| |
2| | 2| | 2| |
1| | 1| | 1| |
+------+ +------+ +------+
8L 5L 3L
You can:
(F)ill the bucket
(E)mpty the bucket
(P)our one bucket into another
(Q)uit
> f
Select a bucket 8, 5, 3, or QUIT:
> 5
Try to get 4L of water into one of these
buckets:
8| |
7| |
6| |
5| | 5|WWWWWW|
4| | 4|WWWWWW|
3| | 3|WWWWWW| 3| |
2| | 2|WWWWWW| 2| |
1| | 1|WWWWWW| 1| |
+------+ +------+ +------+
8L 5L 3L
--snip--
The waterInBucket
variable stores a dictionary that represents the state of the water buckets. The keys to this dictionary are the strings '8'
, '5'
, and '3'
(representing the buckets), and their values are integers (representing the liters of water in that bucket).
Lines 48 to 59 use this dictionary to render the buckets and water on the screen. The waterDisplay
list contains either 'WWWWWW'
(representing water) or ' '
(representing air) and is passed to the format()
string method. The first eight strings in the waterDisplay
list fill the eight-liter bucket, the next five strings the five-liter bucket, and the final three strings the three-liter bucket.
1. """Water Bucket Puzzle, by Al Sweigart [email protected]
2. A water pouring puzzle.
3. More info: https://en.wikipedia.org/wiki/Water_pouring_puzzle
4. This code is available at https://nostarch.com/big-book-small-python-programming
5. Tags: large, game, math, puzzle"""
6.
7. import sys
8.
9.
10. print('Water Bucket Puzzle, by Al Sweigart [email protected]')
11.
12. GOAL = 4 # The exact amount of water to have in a bucket to win.
13. steps = 0 # Keep track of how many steps the player made to solve this.
14.
15. # The amount of water in each bucket:
16. waterInBucket = {'8': 0, '5': 0, '3': 0}
17.
18. while True: # Main game loop.
19. # Display the current state of the buckets:
20. print()
21. print('Try to get ' + str(GOAL) + 'L of water into one of these')
22. print('buckets:')
23.
24. waterDisplay = [] # Contains strings for water or empty space.
25.
26. # Get the strings for the 8L bucket:
27. for i in range(1, 9):
28. if waterInBucket['8'] < i:
29. waterDisplay.append(' ') # Add empty space.
30. else:
31. waterDisplay.append('WWWWWW') # Add water.
32.
33. # Get the strings for the 5L bucket:
34. for i in range(1, 6):
35. if waterInBucket['5'] < i:
36. waterDisplay.append(' ') # Add empty space.
37. else:
38. waterDisplay.append('WWWWWW') # Add water.
39.
40. # Get the strings for the 3L bucket:
41. for i in range(1, 4):
42. if waterInBucket['3'] < i:
43. waterDisplay.append(' ') # Add empty space.
44. else:
45. waterDisplay.append('WWWWWW') # Add water.
46.
47. # Display the buckets with the amount of water in each one:
48. print('''
49. 8|{7}|
50. 7|{6}|
51. 6|{5}|
52. 5|{4}| 5|{12}|
53. 4|{3}| 4|{11}|
54. 3|{2}| 3|{10}| 3|{15}|
55. 2|{1}| 2|{9}| 2|{14}|
56. 1|{0}| 1|{8}| 1|{13}|
57. +------+ +------+ +------+
58. 8L 5L 3L
59. '''.format(*waterDisplay))
60.
61. # Check if any of the buckets has the goal amount of water:
62. for waterAmount in waterInBucket.values():
63. if waterAmount == GOAL:
64. print('Good job! You solved it in', steps, 'steps!')
65. sys.exit()
66.
67. # Let the player select an action to do with a bucket:
68. print('You can:')
69. print(' (F)ill the bucket')
70. print(' (E)mpty the bucket')
71. print(' (P)our one bucket into another')
72. print(' (Q)uit')
73.
74. while True: # Keep asking until the player enters a valid action.
75. move = input('> ').upper()
76. if move == 'QUIT' or move == 'Q':
77. print('Thanks for playing!')
78. sys.exit()
79.
80. if move in ('F', 'E', 'P'):
81. break # Player has selected a valid action.
82. print('Enter F, E, P, or Q')
83.
84. # Let the player select a bucket:
85. while True: # Keep asking until valid bucket entered.
86. print('Select a bucket 8, 5, 3, or QUIT:')
87. srcBucket = input('> ').upper()
88.
89. if srcBucket == 'QUIT':
90. print('Thanks for playing!')
91. sys.exit()
92.
93. if srcBucket in ('8', '5', '3'):
94. break # Player has selected a valid bucket.
95.
96. # Carry out the selected action:
97. if move == 'F':
98. # Set the amount of water to the max size.
99. srcBucketSize = int(srcBucket)
100. waterInBucket[srcBucket] = srcBucketSize
101. steps += 1
102.
103. elif move == 'E':
104. waterInBucket[srcBucket] = 0 # Set water amount to nothing.
105. steps += 1
106.
107. elif move == 'P':
108. # Let the player select a bucket to pour into:
109. while True: # Keep asking until valid bucket entered.
110. print('Select a bucket to pour into: 8, 5, or 3')
111. dstBucket = input('> ').upper()
112. if dstBucket in ('8', '5', '3'):
113. break # Player has selected a valid bucket.
114.
115. # Figure out the amount to pour:
116. dstBucketSize = int(dstBucket)
117. emptySpaceInDstBucket = dstBucketSize - waterInBucket[dstBucket]
118. waterInSrcBucket = waterInBucket[srcBucket]
119. amountToPour = min(emptySpaceInDstBucket, waterInSrcBucket)
120.
121. # Pour out water from this bucket:
122. waterInBucket[srcBucket] -= amountToPour
123.
124. # Put the poured out water into the other bucket:
125. waterInBucket[dstBucket] += amountToPour
126. steps += 1
127.
128. elif move == 'C':
129. pass # If the player selected Cancel, do nothing.
After entering the source code and running it a few times, try making experimental changes to it. On your own, you can also try to figure out how to do the following:
Try to find the answers to the following questions. Experiment with some modifications to the code and rerun the program to see what effect the changes have.
waterInBucket[srcBucket] = 0
on line 104 to waterInBucket[srcBucket] = 1
?{'8': 0, '5': 0, '3': 0}
on line 16 to {'8': 0, '5': 4, '3': 0}
?{'8': 0, '5': 0, '3': 0}
on line 16 to {'8': 9, '5': 0, '3': 0}
?