|
| 1 | +//! This example demonstrates the BFS bidirectional algorithm, |
| 2 | +//! and compares it with the regular BFS algorithm. |
| 3 | +
|
| 4 | +use pathfinding::prelude::{bfs, bfs_bidirectional}; |
| 5 | +use std::ops::Add; |
| 6 | +use std::time::Instant; |
| 7 | + |
| 8 | +const SIZE: isize = 64; |
| 9 | +const BOTTOM_LEFT: P = P(0, 0); |
| 10 | +const TOP_RIGHT: P = P(SIZE, SIZE); |
| 11 | +const CENTER: P = P(SIZE / 2, SIZE / 2); |
| 12 | + |
| 13 | +#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] |
| 14 | +struct P(isize, isize); |
| 15 | + |
| 16 | +impl Add for P { |
| 17 | + type Output = Self; |
| 18 | + |
| 19 | + fn add(self, other: Self) -> Self { |
| 20 | + P(self.0 + other.0, self.1 + other.1) |
| 21 | + } |
| 22 | +} |
| 23 | + |
| 24 | +fn successors(p: &P) -> Vec<P> { |
| 25 | + [P(0, 1), P(0, -1), P(1, 0), P(-1, 0)] |
| 26 | + .into_iter() |
| 27 | + .map(|delta| p.clone() + delta) |
| 28 | + .filter(|p| p.0 >= 0 && p.0 <= SIZE && p.1 >= 0 && p.1 <= SIZE) |
| 29 | + .collect() |
| 30 | +} |
| 31 | + |
| 32 | +fn main() { |
| 33 | + run_corner_to_corner(); |
| 34 | + run_center_to_corner(); |
| 35 | +} |
| 36 | + |
| 37 | +/// Corner to corner: |
| 38 | +/// ================= |
| 39 | +/// |
| 40 | +/// In this case both algorithms will perform similarly. |
| 41 | +/// In fact, regular BFS will perform slightly better, since the algorithm is slightly simpler. |
| 42 | +/// |
| 43 | +/// We can understand this in terms of the number of points that need to be searched in order to reach |
| 44 | +/// the goal. In the below diagrams this corresponds to the area covered in the final snapshot. |
| 45 | +/// |
| 46 | +/// In both cases every point gets searched - the entire area is filled. For this reason we can intuitively see that |
| 47 | +/// regular BFS and bidirectional BFS will perform similarly. |
| 48 | +/// |
| 49 | +/// Regular BFS: |
| 50 | +/// ============ |
| 51 | +/// |
| 52 | +/// $---------$ $---------$ $---------$ $---------$ $---------$ $---------$ |
| 53 | +/// | G| | G| | G| | G| |FFFFFFF G| |FFFFFFFFG| |
| 54 | +/// | | | | | | | | |FFFFFFFF | |FFFFFFFFF| |
| 55 | +/// | | | | | | | | |FFFFFFFFF| |FFFFFFFFF| |
| 56 | +/// | | | | | | | | |FFFFFFFFF| |FFFFFFFFF| |
| 57 | +/// | | => | | => | | => | | => ... => |FFFFFFFFF| => |FFFFFFFFF| |
| 58 | +/// | | | | | | |F | |FFFFFFFFF| |FFFFFFFFF| |
| 59 | +/// | | | | |F | |FF | |FFFFFFFFF| |FFFFFFFFF| |
| 60 | +/// | | |F | |FF | |FFF | |FFFFFFFFF| |FFFFFFFFF| |
| 61 | +/// |S | |SF | |SFF | |SFFF | |SFFFFFFFF| |SFFFFFFFF| |
| 62 | +/// $---------$ $---------$ $---------$ $---------$ $---------$ $---------$ |
| 63 | +/// |
| 64 | +/// Bidirectional BFS: |
| 65 | +/// ================== |
| 66 | +/// |
| 67 | +/// $---------$ $---------$ $---------$ $---------$ $---------$ $---------$ |
| 68 | +/// | G| | BG| | BBG| | BBBG| | BBBBBBBG| |FBBBBBBBG| |
| 69 | +/// | | | B| | BB| | BBB| |F BBBBBBB| |FFBBBBBBB| |
| 70 | +/// | | | | | B| | BB| |FF BBBBBB| |FFFBBBBBB| |
| 71 | +/// | | | | | | | B| |FFF BBBBB| |FFFFBBBBB| |
| 72 | +/// | | => | | => | | => | | => ... => |FFFF BBBB| => |FFFFFBBBB| |
| 73 | +/// | | | | | | |F | |FFFFF BBB| |FFFFFFBBB| |
| 74 | +/// | | | | |F | |FF | |FFFFFF BB| |FFFFFFFBB| |
| 75 | +/// | | |F | |FF | |FFF | |FFFFFFF B| |FFFFFFFFB| |
| 76 | +/// |S | |SF | |SFF | |SFFF | |SFFFFFFF | |SFFFFFFFF| |
| 77 | +/// $---------$ $---------$ $---------$ $---------$ $---------$ $---------$ |
| 78 | +fn run_corner_to_corner() { |
| 79 | + let instant = Instant::now(); |
| 80 | + bfs(&BOTTOM_LEFT, &successors, |p| *p == TOP_RIGHT); |
| 81 | + let duration_bfs = instant.elapsed(); |
| 82 | + |
| 83 | + let instant = Instant::now(); |
| 84 | + bfs_bidirectional(&BOTTOM_LEFT, &TOP_RIGHT, successors, successors); |
| 85 | + let duration_bfs_bidirectional = instant.elapsed(); |
| 86 | + |
| 87 | + print!( |
| 88 | + " |
| 89 | +Corner to Corner |
| 90 | +================ |
| 91 | +BFS took {duration_bfs:?} |
| 92 | +Bidirectional BFS took {duration_bfs_bidirectional:?} |
| 93 | +" |
| 94 | + ); |
| 95 | +} |
| 96 | + |
| 97 | +/// Center to corner: |
| 98 | +/// ================= |
| 99 | +/// |
| 100 | +/// In this case bidirectional BFS will outperform regular BFS. |
| 101 | +/// |
| 102 | +/// We can understand this in terms of the number of points that need to be searched in order to reach |
| 103 | +/// the goal. In the below diagrams this corresponds to the area covered in the final snapshot. |
| 104 | +/// |
| 105 | +/// In this case for the regular BFS every point still needs to be searched - again, the entire area is filled. |
| 106 | +/// However, for the bidirectional BFS some points remain unsearched - the entire area is not filled. For this |
| 107 | +/// reason we can intuitively see that bidirectional BFS will outperform regular BFS here. |
| 108 | +/// |
| 109 | +/// Regular BFS: |
| 110 | +/// ============ |
| 111 | +/// |
| 112 | +/// $---------$ $---------$ $---------$ $---------$ $---------$ |
| 113 | +/// | G| | G| | G| | FFFFFFFG| |FFFFFFFFG| |
| 114 | +/// | | | | | | |FFFFFFFFF| |FFFFFFFFF| |
| 115 | +/// | | | F | | F | |FFFFFFFFF| |FFFFFFFFF| |
| 116 | +/// | | | FFF | | FFF | |FFFFFFFFF| |FFFFFFFFF| |
| 117 | +/// | S | => | FFSFF | => | FFSFF | => ... => |FFFFSFFFF| => |FFFFSFFFF| |
| 118 | +/// | | | FFF | | FFF | |FFFFFFFFF| |FFFFFFFFF| |
| 119 | +/// | | | F | | F | |FFFFFFFFF| |FFFFFFFFF| |
| 120 | +/// | | | | | | |FFFFFFFFF| |FFFFFFFFF| |
| 121 | +/// | | | | | | | FFFFFFF | |FFFFFFFFF| |
| 122 | +/// $---------$ $---------$ $---------$ $---------$ $---------$ |
| 123 | +/// |
| 124 | +/// Bidirectional BFS: |
| 125 | +/// ================== |
| 126 | +/// |
| 127 | +/// $---------$ $---------$ $---------$ $---------$ $---------$ |
| 128 | +/// | G| | BG| | BBG| | BBBG| | FBBBG| |
| 129 | +/// | | | B| | BB| | F BBB| | FFFBBB| |
| 130 | +/// | | | | | F | | FFF B| | FFFFFBB| |
| 131 | +/// | | | F | | FFF | | FFFFF | | FFFFFFFB| |
| 132 | +/// | S | => | FSF | => | FFSFF | => | FFFSFFF | => |FFFFSFFFF| |
| 133 | +/// | | | F | | FFF | | FFFFF | | FFFFFFF | |
| 134 | +/// | | | | | F | | FFF | | FFFFF | |
| 135 | +/// | | | | | | | F | | FFF | |
| 136 | +/// | | | | | | | | | F | |
| 137 | +/// $---------$ $---------$ $---------$ $---------$ $---------$ |
| 138 | +fn run_center_to_corner() { |
| 139 | + let instant = Instant::now(); |
| 140 | + bfs(&CENTER, &successors, |p| *p == TOP_RIGHT); |
| 141 | + let duration_bfs = instant.elapsed(); |
| 142 | + |
| 143 | + let instant = Instant::now(); |
| 144 | + bfs_bidirectional(&CENTER, &TOP_RIGHT, successors, successors); |
| 145 | + let duration_bfs_bidirectional = instant.elapsed(); |
| 146 | + |
| 147 | + print!( |
| 148 | + " |
| 149 | +Center to Corner |
| 150 | +================ |
| 151 | +BFS took {duration_bfs:?} |
| 152 | +Bidirectional BFS took {duration_bfs_bidirectional:?} |
| 153 | +" |
| 154 | + ); |
| 155 | +} |
0 commit comments