Skip to content

Commit 71a354d

Browse files
committed
Doc strings and unit tests.
1 parent a09db6f commit 71a354d

File tree

2 files changed

+77
-21
lines changed

2 files changed

+77
-21
lines changed

algorithms/a_star_path_finding.py

+42-21
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ def __init__(self, x, y, reachable):
55
"""
66
Initialize new cell
77
8+
@param reachable is cell reachable? not a wall?
89
@param x cell x coordinate
910
@param y cell y coordinate
10-
@param reachable is cell reachable? not a wall?
11+
@param g cost to move from the starting cell to this cell.
12+
@param h estimation of the cost to move from this cell
13+
to the ending cell.
14+
@param f f = g + h
1115
"""
1216
self.reachable = reachable
1317
self.x = x
@@ -19,32 +23,43 @@ def __init__(self, x, y, reachable):
1923

2024
class AStar(object):
2125
def __init__(self):
26+
# open list
2227
self.opened = []
2328
heapq.heapify(self.opened)
29+
# visited cells list
2430
self.closed = set()
31+
# grid cells
2532
self.cells = []
26-
self.grid_height = 6
27-
self.grid_width = 6
33+
self.grid_height = None
34+
self.grid_width = None
2835

29-
def init_grid(self):
30-
walls = ((0, 5), (1, 0), (1, 1), (1, 5), (2, 3),
31-
(3, 1), (3, 2), (3, 5), (4, 1), (4, 4), (5, 1))
36+
def init_grid(self, width, height, walls, start, end):
37+
"""
38+
Prepare grid cells, walls.
39+
40+
@param width grid's width.
41+
@param height grid's height.
42+
@param walls list of wall x,y tuples.
43+
@param start grid starting point x,y tuple.
44+
@param end grid ending point x,y tuple.
45+
"""
46+
self.grid_height = height
47+
self.grid_width = width
3248
for x in range(self.grid_width):
3349
for y in range(self.grid_height):
3450
if (x, y) in walls:
3551
reachable = False
3652
else:
3753
reachable = True
3854
self.cells.append(Cell(x, y, reachable))
39-
self.start = self.get_cell(0, 0)
40-
self.end = self.get_cell(5, 5)
55+
self.start = self.get_cell(*start)
56+
self.end = self.get_cell(*end)
4157

4258
def get_heuristic(self, cell):
4359
"""
4460
Compute the heuristic value H for a cell: distance between
4561
this cell and the ending cell multiply by 10.
4662
47-
@param cell
4863
@returns heuristic value H
4964
"""
5065
return 10 * (abs(cell.x - self.end.x) + abs(cell.y - self.end.y))
@@ -65,7 +80,7 @@ def get_adjacent_cells(self, cell):
6580
from the one on the right.
6681
6782
@param cell get adjacent cells for this cell
68-
@returns adjacent cells list
83+
@returns adjacent cells list.
6984
"""
7085
cells = []
7186
if cell.x < self.grid_width-1:
@@ -78,11 +93,16 @@ def get_adjacent_cells(self, cell):
7893
cells.append(self.get_cell(cell.x, cell.y+1))
7994
return cells
8095

81-
def display_path(self):
96+
def get_path(self):
8297
cell = self.end
98+
path = [(cell.x, cell.y)]
8399
while cell.parent is not self.start:
84100
cell = cell.parent
85-
print 'path: cell: %d,%d' % (cell.x, cell.y)
101+
path.append((cell.x, cell.y))
102+
103+
path.append((self.start.x, self.start.y))
104+
path.reverse()
105+
return path
86106

87107
def compare(self, cell1, cell2):
88108
"""
@@ -97,7 +117,7 @@ def compare(self, cell1, cell2):
97117
elif cell1.f > cell2.f:
98118
return 1
99119
return 0
100-
120+
101121
def update_cell(self, adj, cell):
102122
"""
103123
Update adjacent cell
@@ -110,18 +130,22 @@ def update_cell(self, adj, cell):
110130
adj.parent = cell
111131
adj.f = adj.h + adj.g
112132

113-
def process(self):
133+
def solve(self):
134+
"""
135+
Solve maze, find path to ending cell.
136+
137+
@returns path or None if not found.
138+
"""
114139
# add starting cell to open heap queue
115140
heapq.heappush(self.opened, (self.start.f, self.start))
116141
while len(self.opened):
117-
# pop cell from heap queue
142+
# pop cell from heap queue
118143
f, cell = heapq.heappop(self.opened)
119144
# add cell to closed list so we don't process it twice
120145
self.closed.add(cell)
121-
# if ending cell, display found path
146+
# if ending cell, return found path
122147
if cell is self.end:
123-
self.display_path()
124-
break
148+
return self.get_path()
125149
# get adjacent cells for cell
126150
adj_cells = self.get_adjacent_cells(cell)
127151
for adj_cell in adj_cells:
@@ -137,7 +161,4 @@ def process(self):
137161
# add adj cell to open list
138162
heapq.heappush(self.opened, (adj_cell.f, adj_cell))
139163

140-
a = AStar()
141-
a.init_grid()
142-
a.process()
143164

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import unittest
2+
import algorithms.a_star_path_finding as pf
3+
4+
class Test(unittest.TestCase):
5+
6+
def setUp(self):
7+
pass
8+
9+
def test_maze(self):
10+
a = pf.AStar()
11+
walls = ((0, 5), (1, 0), (1, 1), (1, 5), (2, 3),
12+
(3, 1), (3, 2), (3, 5), (4, 1), (4, 4), (5, 1))
13+
a.init_grid(6, 6, walls, (0, 0), (5, 5))
14+
path = a.solve()
15+
self.assertEqual(path, [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 4),
16+
(2, 4), (3, 4), (3, 3), (4, 3), (5, 3), (5, 4),
17+
(5, 5)])
18+
19+
def test_maze_no_walls(self):
20+
a = pf.AStar()
21+
walls = ()
22+
a.init_grid(6, 6, walls, (0, 0), (5, 5))
23+
path = a.solve()
24+
self.assertEqual(len(path), 11)
25+
26+
def test_maze_no_solution(self):
27+
a = pf.AStar()
28+
walls = ((0, 5), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5),
29+
(2, 3), (3, 1), (3, 2), (3, 5), (4, 1), (4, 4), (5, 1))
30+
a.init_grid(6, 6, walls, (0, 0), (5, 5))
31+
self.assertIsNone(a.solve())
32+
33+
if __name__ == '__main__':
34+
unittest.main()
35+

0 commit comments

Comments
 (0)