-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbridge.py
90 lines (81 loc) · 3.35 KB
/
bridge.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
"""We represent our starting state as frozensets of people (indicated with the time
they need to cross the bridge) on one side of the river('here'), plus the light
they need to carry when crossing. The goal is to get all people to the other side,
that is, no one remaining in 'here', in the shortest time possible."""
def bridge_problem2(here):
"Find the fastest (least path cost) path to the goal in the bridge problem."
here = frozenset(here) | frozenset(['light'])
explored = set() # set of states we have visited
# State will be a (people_here, people_there)
# E.g. ({1, 2, 5, 10, 'light'}, {})
frontier = [ [(here, frozenset())] ] # ordered list of paths we have blazed
while frontier:
path = frontier.pop(0)
here1, there1 = state1 = final_state(path)
if not here1 or (len(here1) == 1 and 'light' in here1):
return path
explored.add(state1)
pcost = path_cost(path)
for (state, action) in bsuccessors2(state1).items():
if state not in explored:
total_cost = pcost + bcost(action)
path2 = path + [(action, total_cost), state]
add_to_frontier(frontier, path2)
return []
bridge_problem = bridge_problem2
def final_state(path):
return path[-1]
def path_cost(path):
"""The total cost of a path (which is stored in a tuple
with the final action."""
# path = [state, (action, total_cost), state, ... ]
if len(path) < 2:
return 0
else:
return path[-2][-1]
def bcost(action):
"""Returns the cost (a number) of an action in the
bridge problem."""
# An action is an (a, b, arrow) tuple; a and b are
# times; arrow is a string.
a, b, arrow = action
return max(a, b)
def add_to_frontier(frontier, path):
"Add path to frontier, replacing costlier path if there is one."
# (This could be done more efficiently)
# Find if there is an old path to the final state of this path.
old = None
for i, p in enumerate(frontier):
if final_state(p) == final_state(path):
old = i
break
if old is not None and path_cost(frontier[old]) < path_cost(path):
return # Old path was better; do nothing
elif old is not None:
del frontier[old] # Old path was worse; delete it
## Now add the new path and re-sort
frontier.append(path)
frontier.sort(key = path_cost)
def bsuccessors2(state):
"""Return a dict of {state:action} pairs. A state is a (here, there) tuple,
where here and there are frozensets of people (indicated by their times) and/or
the light."""
here, there = state
if 'light' in here:
return dict(((here - frozenset([a, b, 'light']),
there | frozenset([a, b, 'light'])),
(a, b, '->'))
for a in here if a is not 'light'
for b in here if b is not 'light')
else:
return dict(((here | frozenset([a, b, 'light']),
there - frozenset([a, b, 'light'])),
(a, b, '<-'))
for a in there if a is not 'light'
for b in there if b is not 'light')
def path_states(path):
"Return a list of states in this path."
return path[::2]
def path_actions(path):
"Return a list of actions in this path."
return path[1::2]