Skip to content

Instantly share code, notes, and snippets.

@Jessime
Last active May 18, 2023 15:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jessime/a1a751aaa35da244b2e76b5809d01740 to your computer and use it in GitHub Desktop.
Save Jessime/a1a751aaa35da244b2e76b5809d01740 to your computer and use it in GitHub Desktop.
To disable a bomb, Detective John McClane must measure out exactly 4 gallons of water, and place the resulting weight on a scale. His tools are yours: a 3-gallon and a 5-­gallon jug—and a single fountain. https://www.popsci.com/solve-water-puzzle-die-hard-3/
from dataclasses import dataclass
from typing import Optional, Callable
def fill_b3(b3, b5):
return 3, b5
def fill_b5(b3, b5):
return b3, 5
def empty_b3(b3, b5):
return 0, b5
def empty_b5(b3, b5):
return b3, 0
def b3_into_b5(b3, b5):
return max(0, b3 - (5 - b5)), min(5, b3+b5)
def b5_into_b3(b3, b5):
return min(3, b3 + b5), max(0, b5 - (3-b3))
ACTIONS = [fill_b3, fill_b5, empty_b3, empty_b5, b3_into_b5, b5_into_b3]
ACTION_2_DESC = {
fill_b3: "Fill bucket3 from the well. End state is: ",
fill_b5: "Fill bucket5 from the well. End state is: ",
empty_b3: "Empty bucket3. End state is: ",
empty_b5: "Empty bucket5. End state is: ",
b3_into_b5: "Move the contents of bucket3 into bucket5. End state is: ",
b5_into_b3: "Move the contents of bucket5 into bucket3. End state is: ",
None: ""
}
@dataclass()
class Node:
state: tuple[int, int]
parent: Optional["Node"] = None
func: Optional[Callable] = None
def find_solution():
seen_states = {(0, 0)}
root = Node((0, 0))
current_level = [root]
while True:
new_level = []
for node in current_level:
for action in ACTIONS:
end_state = action(*node.state)
if end_state not in seen_states:
seen_states.add(end_state)
new_node = Node(end_state, node, action)
new_level.append(new_node)
if sum(end_state) == 4:
return new_node
current_level = new_level
def print_lineage(node):
parent = node.parent
lineage = []
while True:
if parent is None:
break
lineage.insert(0, node)
node = node.parent
parent = node.parent
for node in lineage:
print(ACTION_2_DESC[node.func], node.state)
if __name__ == "__main__":
print_lineage(find_solution())
Fill bucket3 from the well. End state is: (3, 0)
Move the contents of bucket3 into bucket5. End state is: (0, 3)
Fill bucket3 from the well. End state is: (3, 3)
Move the contents of bucket3 into bucket5. End state is: (1, 5)
Empty bucket5. End state is: (1, 0)
Move the contents of bucket3 into bucket5. End state is: (0, 1)
Fill bucket3 from the well. End state is: (3, 1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment