Skip to content

Commit 3c33f3a

Browse files
Create puzzle.py
1 parent 7253102 commit 3c33f3a

1 file changed

Lines changed: 205 additions & 0 deletions

File tree

puzzle.py

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# Q2: Robot Evacuation Planning using Best First Search
2+
3+
# Grid dimensions: 10 rows, 20 columns
4+
# 0 = Walkable (Hallway/Room interior)
5+
# 1 = Wall/Blocked
6+
7+
# Approximated Floor Plan based on image
8+
# Row 4 is the main hallway
9+
# Entry at (8, 4), Exit at (4, 18)
10+
11+
grid = [
12+
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], # 0: Top Wall
13+
[1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1], # 1: Rooms
14+
[1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1], # 2: Rooms
15+
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], # 3: Wall separating rooms from hall
16+
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # 4: Main Hallway (Exit at end)
17+
[1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1], # 5: Structures below hall
18+
[1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1], # 6: More structures
19+
[1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1], # 7: Walls
20+
[1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], # 8: Entry area (at 8,4)
21+
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] # 9: Bottom Wall
22+
]
23+
24+
rows = 10
25+
cols = 20
26+
27+
# Entry and Exit
28+
START = (8, 4) # Entry (Row 8, Col 4)
29+
GOAL = (4, 18) # Exit (Row 4, Col 18)
30+
31+
class Node:
32+
def __init__(self, state, parent=None, action=None, path_cost=0):
33+
self.state = state # (row, col)
34+
self.parent = parent
35+
self.action = action # "Up", "Down", "Left", "Right"
36+
self.path_cost = path_cost
37+
38+
class PriorityQueue:
39+
def __init__(self, f):
40+
self.data = []
41+
self.f = f
42+
43+
def add(self, node):
44+
self.data.append(node)
45+
self.data.sort(key=self.f)
46+
47+
def pop(self):
48+
return self.data.pop(0)
49+
50+
def top(self):
51+
return self.data[0]
52+
53+
def is_empty(self):
54+
return len(self.data) == 0
55+
56+
class Problem:
57+
def __init__(self, initial, goal, grid):
58+
self.initial = initial
59+
self.goal = goal
60+
self.grid = grid
61+
62+
def is_goal(self, state):
63+
return state == self.goal
64+
65+
def ACTIONS(self, state):
66+
r, c = state
67+
actions = []
68+
# Down, Up, Right, Left
69+
# Using simple step cost 1 for all moves
70+
moves = [
71+
("Down", (1, 0)),
72+
("Up", (-1, 0)),
73+
("Right", (0, 1)),
74+
("Left", (0, -1))
75+
]
76+
77+
for name, (dr, dc) in moves:
78+
nr, nc = r + dr, c + dc
79+
# Check boundaries and walls
80+
if 0 <= nr < rows and 0 <= nc < cols:
81+
if self.grid[nr][nc] == 0: # 0 is walkable
82+
actions.append(name)
83+
return actions
84+
85+
def RESULT(self, state, action):
86+
r, c = state
87+
if action == "Down": return (r + 1, c)
88+
if action == "Up": return (r - 1, c)
89+
if action == "Right": return (r, c + 1)
90+
if action == "Left": return (r, c - 1)
91+
return state
92+
93+
def ACTION_COST(self, s, action, s_prime):
94+
return 1 # Uniform cost for grid movement
95+
96+
# Heuristic: Manhattan Distance
97+
def heuristic(node):
98+
r1, c1 = node.state
99+
r2, c2 = GOAL
100+
# h(n) = |x1 - x2| + |y1 - y2|
101+
return abs(r1 - r2) + abs(c1 - c2)
102+
103+
# Evaluation function f(n) = g(n) for Uniform Cost Search (UCS)
104+
def f(node):
105+
# Justification: UCS uses path cost g(n) to find the optimal path.
106+
return node.path_cost
107+
108+
def EXPAND(problem, node):
109+
s = node.state
110+
for action in problem.ACTIONS(s):
111+
s_prime = problem.RESULT(s, action)
112+
cost = node.path_cost + problem.ACTION_COST(s, action, s_prime)
113+
yield Node(state=s_prime, parent=node, action=action, path_cost=cost)
114+
115+
def BEST_FIRST_SEARCH(problem, f):
116+
node = Node(problem.initial)
117+
frontier = PriorityQueue(f)
118+
frontier.add(node)
119+
120+
# 2D reached table
121+
reached = [[None for _ in range(cols)] for _ in range(rows)]
122+
reached[node.state[0]][node.state[1]] = node
123+
124+
explored = 0
125+
126+
while not frontier.is_empty():
127+
node = frontier.pop()
128+
explored += 1
129+
130+
if problem.is_goal(node.state):
131+
return node, explored
132+
133+
for child in EXPAND(problem, node):
134+
r, c = child.state
135+
if reached[r][c] is None or child.path_cost < reached[r][c].path_cost:
136+
reached[r][c] = child
137+
frontier.add(child)
138+
139+
return None, explored
140+
141+
def get_path(node):
142+
path = []
143+
while node:
144+
path.append(node.state)
145+
node = node.parent
146+
return path[::-1]
147+
148+
def print_grid_with_path(grid, path):
149+
print("\nEvaluated Path on Grid:")
150+
path_set = set(path)
151+
152+
# Header
153+
print(" ", end="")
154+
for c in range(cols): print(f"{c%10}", end=" ")
155+
print()
156+
157+
for r in range(rows):
158+
print(f"{r:<2} ", end="")
159+
for c in range(cols):
160+
if (r, c) == START:
161+
print("S", end=" ")
162+
elif (r, c) == GOAL:
163+
print("E", end=" ")
164+
elif (r, c) in path_set:
165+
print(".", end=" ") # Path marker
166+
elif grid[r][c] == 1:
167+
print("#", end=" ") # Wall
168+
else:
169+
print(" ", end=" ") # Empty space
170+
print()
171+
172+
if __name__ == "__main__":
173+
problem = Problem(START, GOAL, grid)
174+
175+
print("Starting Uniform Cost Search (UCS)...")
176+
print(f"Start: {START}, Goal: {GOAL}")
177+
178+
solution, explored = BEST_FIRST_SEARCH(problem, f)
179+
180+
if solution:
181+
path = get_path(solution)
182+
print("\nGoal Found!")
183+
print(f"Path Length: {len(path)}")
184+
print(f"Total Cost: {solution.path_cost}")
185+
print(f"Nodes Explored: {explored}")
186+
187+
print_grid_with_path(grid, path)
188+
189+
print("\nPath Steps:")
190+
curr = solution
191+
steps = []
192+
while curr.parent:
193+
steps.append(f"{curr.action} -> {curr.state}")
194+
curr = curr.parent
195+
for s in reversed(steps):
196+
print(s)
197+
198+
print("\nEvaluation Cost Function Justification:")
199+
print("Function: Path Cost f(n) = g(n)")
200+
print("Reason: Uniform Cost Search expands nodes based on total path cost from the start.")
201+
print("This guarantees shortest path finding in a grid with uniform step costs,")
202+
print("exploring in 'waves' rather than just aiming for the goal.")
203+
204+
else:
205+
print("No path found!")

0 commit comments

Comments
 (0)