Skip to content

Commit 7571001

Browse files
authored
Merge pull request #73 from PraneethJain/bidirectional-bfs
Added bi-directional breadth-first-search
2 parents 20a258c + a07d515 commit 7571001

File tree

3 files changed

+90
-4
lines changed

3 files changed

+90
-4
lines changed

pathfinding/finder/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__all__ = ['a_star', 'best_first', 'bi_a_star', 'breadth_first', 'dijkstra',
2-
'finder', 'ida_star']
1+
__all__ = ['a_star', 'best_first', 'bi_a_star', 'bi_breadth_first',
2+
'breadth_first', 'dijkstra', 'finder', 'ida_star']
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import time
2+
from collections import deque
3+
4+
from .finder import BY_END, BY_START, Finder, MAX_RUNS, TIME_LIMIT
5+
from ..core.diagonal_movement import DiagonalMovement
6+
from ..core.util import bi_backtrace
7+
8+
9+
class BiBreadthFirstFinder(Finder):
10+
"""
11+
Bidirectional Breadth-First-Search (Bi-BFS)
12+
"""
13+
14+
def __init__(
15+
self,
16+
diagonal_movement=DiagonalMovement.never,
17+
time_limit=TIME_LIMIT,
18+
max_runs=MAX_RUNS,
19+
):
20+
super(BiBreadthFirstFinder, self).__init__(
21+
weighted=False,
22+
diagonal_movement=diagonal_movement,
23+
time_limit=time_limit,
24+
max_runs=max_runs,
25+
)
26+
27+
def _search_level(self, grid, queue, opened_by, looking_for):
28+
"""
29+
Search one level from the given queue.
30+
"""
31+
level_size = len(queue)
32+
for _ in range(level_size):
33+
self.runs += 1
34+
self.keep_running()
35+
36+
node = queue.popleft()
37+
node.closed = True
38+
39+
neighbors = self.find_neighbors(grid, node)
40+
for neighbor in neighbors:
41+
if neighbor.opened == opened_by:
42+
continue
43+
if neighbor.opened == looking_for:
44+
if opened_by == BY_START:
45+
return bi_backtrace(node, neighbor)
46+
else:
47+
return bi_backtrace(neighbor, node)
48+
49+
neighbor.opened = opened_by
50+
neighbor.parent = node
51+
queue.append(neighbor)
52+
return None
53+
54+
def find_path(self, start, end, grid):
55+
"""
56+
Find a path from start to end node on grid using Bi-BFS
57+
:param start: start node
58+
:param end: end node
59+
:param grid: grid that stores all possible nodes
60+
:return:
61+
"""
62+
self.clean_grid(grid)
63+
64+
self.start_time = time.time()
65+
self.runs = 0
66+
67+
start_queue = deque([start])
68+
start.opened = BY_START
69+
start.parent = None
70+
71+
end_queue = deque([end])
72+
end.opened = BY_END
73+
end.parent = None
74+
75+
while start_queue and end_queue:
76+
path = self._search_level(grid, start_queue, BY_START, BY_END)
77+
if path:
78+
return path, self.runs
79+
80+
path = self._search_level(grid, end_queue, BY_END, BY_START)
81+
if path:
82+
return path, self.runs
83+
84+
# failed to find path
85+
return [], self.runs

test/test_path.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from pathfinding.finder.finder import ExecutionTimeException
1313
from pathfinding.finder.ida_star import IDAStarFinder
1414
from pathfinding.finder.msp import MinimumSpanningTree
15+
from pathfinding.finder.bi_breadth_first import BiBreadthFirstFinder
1516

1617
import pytest
1718

@@ -21,8 +22,8 @@
2122
# test scenarios from Pathfinding.JS
2223
scenarios = os.path.join(BASE_PATH, 'path_test_scenarios.json')
2324
data = json.load(open(scenarios, 'r', encoding='utf-8'))
24-
finders = [AStarFinder, BestFirst, BiAStarFinder, DijkstraFinder,
25-
IDAStarFinder, BreadthFirstFinder, MinimumSpanningTree]
25+
finders = [AStarFinder, BestFirst, BiAStarFinder, BiBreadthFirstFinder,
26+
DijkstraFinder, IDAStarFinder, BreadthFirstFinder, MinimumSpanningTree]
2627
TIME_LIMIT = 10 # give it a 10 second limit.
2728

2829

0 commit comments

Comments
 (0)