From 52de927f18b22e3c7b12bb675aa55950ff314608 Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Sun, 23 Nov 2025 12:08:41 +0100 Subject: [PATCH 01/16] Added straightforward implementation of ALT algorithm in ALTSolver --- src/solver/alt.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++ src/solver/mod.rs | 1 + 2 files changed, 73 insertions(+) create mode 100644 src/solver/alt.rs diff --git a/src/solver/alt.rs b/src/solver/alt.rs new file mode 100644 index 0000000..5e18850 --- /dev/null +++ b/src/solver/alt.rs @@ -0,0 +1,72 @@ +use std::i32; + +use crate::solver::dijkstra::DijkstraSolver; +use grid_util::{Point, SimpleValueGrid, ValueGrid}; +use smallvec::SmallVec; + +use crate::{pathing_grid::PathingGrid, solver::GridSolver, N_SMALLVEC_SIZE}; + +#[derive(Clone, Debug)] +pub struct ALTSolver { + landmarks: Vec, + landmark_distances: Vec>, +} +impl ALTSolver { + pub fn new( + landmarks: Vec, + grid: &mut PathingGrid, + ) -> ALTSolver { + let d = DijkstraSolver; + let mut landmark_distances = Vec::new(); + for landmark in &landmarks { + let mut dists = SimpleValueGrid::new(grid.width(), grid.height(), i32::MAX); + let mut ct = grid.context.lock().unwrap(); + ct.astar_jps( + &landmark, + |parent, node| d.successors(grid, *parent, node, &|_| false), + |_| 0, + |_| false, + ) + .map(|(v, _c)| v); + for (p, (_, c)) in &ct.parents { + dists.set_point(*p, *c); + } + landmark_distances.push(dists); + } + ALTSolver { + landmarks, + landmark_distances, + } + } +} + +impl GridSolver for ALTSolver { + type Successors = SmallVec<[(Point, i32); N_SMALLVEC_SIZE]>; + + fn successors( + &self, + grid: &PathingGrid, + _parent: Option<&Point>, + node: &Point, + _goal: &F, + ) -> Self::Successors + where + F: Fn(&Point) -> bool, + { + grid.neighborhood_points_and_cost(node) + } + + /// Just the normal cost times a heuristic factor. + fn heuristic( + &self, + _: &PathingGrid, + p1: &Point, + p2: &Point, + ) -> i32 { + self.landmark_distances + .iter() + .map(|x| (x.get_point(*p1) - x.get_point(*p2)).abs()) + .max() + .unwrap() + } +} diff --git a/src/solver/mod.rs b/src/solver/mod.rs index fc132cb..0d33bec 100644 --- a/src/solver/mod.rs +++ b/src/solver/mod.rs @@ -1,6 +1,7 @@ use crate::{pathing_grid::PathingGrid, waypoints_to_path, C, D, E, EQUAL_EDGE_COST}; use grid_util::Point; +pub mod alt; pub mod astar; pub mod dijkstra; pub mod jps; From 58629bfaeca94e49d865d7a70e8366232ab08df1 Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Sun, 23 Nov 2025 12:23:18 +0100 Subject: [PATCH 02/16] Much improved ALTSolver speed by switching from vec of grids to grid of (small-) vecs --- src/astar_jps.rs | 4 ++-- src/solver/alt.rs | 31 ++++++++++++++++++++----------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/astar_jps.rs b/src/astar_jps.rs index 507ac06..47adaf9 100644 --- a/src/astar_jps.rs +++ b/src/astar_jps.rs @@ -97,8 +97,8 @@ where /// [AstarContext] represents the search fringe and node parent map, facilitating reuse of memory allocations. #[derive(Clone, Debug)] pub struct SearchContext { - fringe: F, - parents: FxIndexMap, + pub(crate) fringe: F, + pub(crate) parents: FxIndexMap, } pub type BinaryHeapSearchContext = SearchContext>>; diff --git a/src/solver/alt.rs b/src/solver/alt.rs index 5e18850..eeee72d 100644 --- a/src/solver/alt.rs +++ b/src/solver/alt.rs @@ -1,15 +1,15 @@ use std::i32; use crate::solver::dijkstra::DijkstraSolver; -use grid_util::{Point, SimpleValueGrid, ValueGrid}; -use smallvec::SmallVec; - use crate::{pathing_grid::PathingGrid, solver::GridSolver, N_SMALLVEC_SIZE}; +use grid_util::{Grid, Point, SimpleGrid, SimpleValueGrid, ValueGrid}; +use smallvec::SmallVec; +use std::iter::zip; #[derive(Clone, Debug)] pub struct ALTSolver { landmarks: Vec, - landmark_distances: Vec>, + landmark_distances: SimpleGrid>, } impl ALTSolver { pub fn new( @@ -17,7 +17,7 @@ impl ALTSolver { grid: &mut PathingGrid, ) -> ALTSolver { let d = DijkstraSolver; - let mut landmark_distances = Vec::new(); + let mut landmark_distances_flipped = Vec::new(); for landmark in &landmarks { let mut dists = SimpleValueGrid::new(grid.width(), grid.height(), i32::MAX); let mut ct = grid.context.lock().unwrap(); @@ -31,7 +31,18 @@ impl ALTSolver { for (p, (_, c)) in &ct.parents { dists.set_point(*p, *c); } - landmark_distances.push(dists); + landmark_distances_flipped.push(dists); + } + let mut landmark_distances = SimpleGrid::new(grid.width(), grid.height(), SmallVec::new()); + for grid in landmark_distances_flipped { + for x in 0..grid.width() as i32 { + for y in 0..grid.height() as i32 { + landmark_distances + .get_mut(x, y) + .unwrap() + .push(grid.get(x, y)); + } + } } ALTSolver { landmarks, @@ -63,10 +74,8 @@ impl GridSolver for ALTSolver { p1: &Point, p2: &Point, ) -> i32 { - self.landmark_distances - .iter() - .map(|x| (x.get_point(*p1) - x.get_point(*p2)).abs()) - .max() - .unwrap() + let g1 = self.landmark_distances.get_point(*p1).unwrap(); + let g2 = self.landmark_distances.get_point(*p2).unwrap(); + zip(g1, g2).map(|(x, y)| (x - y).abs()).max().unwrap() } } From 840239a8f61f8ef0f22f75c4baba7ff1fd3900e5 Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Sun, 23 Nov 2025 12:52:53 +0100 Subject: [PATCH 03/16] Added benchmark of ALT, using primitive monte carlo generation of landmarks --- benches/comparison_bench.rs | 53 ++++++++++++++++++++++++++++++++++--- src/solver/alt.rs | 1 - 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/benches/comparison_bench.rs b/benches/comparison_bench.rs index bdbeaac..5ad1951 100644 --- a/benches/comparison_bench.rs +++ b/benches/comparison_bench.rs @@ -1,11 +1,14 @@ use criterion::{criterion_group, criterion_main, Criterion}; use grid_pathfinding::{ pathing_grid::PathingGrid, - solver::{astar::AstarSolver, dijkstra::DijkstraSolver, jps::JPSSolver, GridSolver}, + solver::{ + alt::ALTSolver, astar::AstarSolver, dijkstra::DijkstraSolver, jps::JPSSolver, GridSolver, + }, Pathfinder, }; use grid_pathfinding_benchmark::*; -use grid_util::grid::ValueGrid; +use grid_util::{grid::ValueGrid, Point}; +use rand::{rngs::StdRng, Rng, SeedableRng}; use smallvec::{smallvec, SmallVec}; use std::hint::black_box; @@ -79,6 +82,49 @@ fn dao_bench_jps(c: &mut Criterion) { } } } +pub fn generate_landmarks_mc( + pathing_grid: &PathingGrid, + number: usize, +) -> Vec { + let mut landmarks = Vec::new(); + let mut rng = StdRng::seed_from_u64(0); + while landmarks.len() < number { + let p = Point::new( + rng.random_range(0..pathing_grid.width() as i32), + rng.random_range(0..pathing_grid.height() as i32), + ); + if pathing_grid.can_move_to_simple(p) { + landmarks.push(p); + } + } + landmarks +} +fn dao_bench_alt(c: &mut Criterion) { + let bench_set = if ALLOW_DIAGONAL { + ["dao/arena", "dao/den312d", "dao/arena2"] + } else { + ["dao/arena", "dao/den009d", "dao/den312d"] + }; + for name in bench_set { + let (bool_grid, scenarios) = get_benchmark(name.to_owned()); + let mut pathing_grid: PathingGrid = + PathingGrid::new(bool_grid.width, bool_grid.height, true); + pathing_grid.grid = bool_grid.clone(); + pathing_grid.generate_components(); + let landmarks = generate_landmarks_mc(&pathing_grid, 128); + let solver = ALTSolver::new(landmarks, &mut pathing_grid); + let diag_str = if ALLOW_DIAGONAL { "8-grid" } else { "4-grid" }; + + c.bench_function(format!("{name}, ALT (MC) {diag_str}").as_str(), |b| { + b.iter(|| { + for (start, end, _) in &scenarios { + black_box(solver.get_path_single_goal(&mut pathing_grid, *start, *end)); + } + }) + }); + } +} + fn dao_bench_astar(c: &mut Criterion) { let bench_set = ["dao/arena", "dao/den009d", "dao/den312d"]; for name in bench_set { @@ -125,7 +171,8 @@ criterion_group!( // dao_bench, dao_bench, // dao_bench_jps, - dao_bench_jps, + // dao_bench_jps, + dao_bench_alt, // dao_bench_astar, // dao_bench_astar, // dao_bench_dijkstra, diff --git a/src/solver/alt.rs b/src/solver/alt.rs index eeee72d..23469c8 100644 --- a/src/solver/alt.rs +++ b/src/solver/alt.rs @@ -67,7 +67,6 @@ impl GridSolver for ALTSolver { grid.neighborhood_points_and_cost(node) } - /// Just the normal cost times a heuristic factor. fn heuristic( &self, _: &PathingGrid, From d370bf69932b8a47c461f830df43f2031167e78b Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Sun, 23 Nov 2025 20:39:57 +0100 Subject: [PATCH 04/16] Introduced generic dao_bench_solver, reusing much of the benchmarking logic for the four implementors of GridSolver --- benches/comparison_bench.rs | 150 +++++++++++++++--------------------- 1 file changed, 61 insertions(+), 89 deletions(-) diff --git a/benches/comparison_bench.rs b/benches/comparison_bench.rs index 5ad1951..0a30646 100644 --- a/benches/comparison_bench.rs +++ b/benches/comparison_bench.rs @@ -46,40 +46,35 @@ fn dao_bench(c: &mut Criterion) { } } -fn dao_bench_jps(c: &mut Criterion) { - let arr: SmallVec<[bool; 2]> = if ALLOW_DIAGONAL { - smallvec![false, true] - } else { - smallvec![false] - }; +fn dao_bench_solver( + c: &mut Criterion, + solver_name: &str, + create_solver: FS, +) where + S: GridSolver, + FS: Fn(&mut PathingGrid) -> S, +{ let bench_set = if ALLOW_DIAGONAL { ["dao/arena", "dao/den312d", "dao/arena2"] } else { ["dao/arena", "dao/den009d", "dao/den312d"] }; - for pruning in arr { - for name in bench_set { - let (bool_grid, scenarios) = get_benchmark(name.to_owned()); - let mut pathing_grid: PathingGrid = - PathingGrid::new(bool_grid.width, bool_grid.height, true); - pathing_grid.grid = bool_grid.clone(); - pathing_grid.generate_components(); - let mut solver = JPSSolver::new(&pathing_grid, pruning); - solver.initialize(&pathing_grid); - let diag_str = if ALLOW_DIAGONAL { "8-grid" } else { "4-grid" }; - let improved_str = if pruning { " (improved pruning)" } else { "" }; + for name in bench_set { + let (bool_grid, scenarios) = get_benchmark(name.to_owned()); + let mut pathing_grid: PathingGrid = + PathingGrid::new(bool_grid.width, bool_grid.height, true); + pathing_grid.grid = bool_grid.clone(); + pathing_grid.generate_components(); + let solver = create_solver(&mut pathing_grid); + let diag_str = if ALLOW_DIAGONAL { "8-grid" } else { "4-grid" }; - c.bench_function( - format!("{name}, JPS {diag_str}{improved_str}").as_str(), - |b| { - b.iter(|| { - for (start, end, _) in &scenarios { - black_box(solver.get_path_single_goal(&mut pathing_grid, *start, *end)); - } - }) - }, - ); - } + c.bench_function(format!("{name}, {solver_name} {diag_str}").as_str(), |b| { + b.iter(|| { + for (start, end, _) in &scenarios { + black_box(solver.get_path_single_goal(&mut pathing_grid, *start, *end)); + } + }) + }); } } pub fn generate_landmarks_mc( @@ -99,81 +94,58 @@ pub fn generate_landmarks_mc( } landmarks } -fn dao_bench_alt(c: &mut Criterion) { - let bench_set = if ALLOW_DIAGONAL { - ["dao/arena", "dao/den312d", "dao/arena2"] + +fn dao_bench_jps(c: &mut Criterion) { + let arr: SmallVec<[bool; 2]> = if ALLOW_DIAGONAL { + smallvec![false, true] } else { - ["dao/arena", "dao/den009d", "dao/den312d"] + smallvec![false] }; - for name in bench_set { - let (bool_grid, scenarios) = get_benchmark(name.to_owned()); - let mut pathing_grid: PathingGrid = - PathingGrid::new(bool_grid.width, bool_grid.height, true); - pathing_grid.grid = bool_grid.clone(); - pathing_grid.generate_components(); - let landmarks = generate_landmarks_mc(&pathing_grid, 128); - let solver = ALTSolver::new(landmarks, &mut pathing_grid); - let diag_str = if ALLOW_DIAGONAL { "8-grid" } else { "4-grid" }; - - c.bench_function(format!("{name}, ALT (MC) {diag_str}").as_str(), |b| { - b.iter(|| { - for (start, end, _) in &scenarios { - black_box(solver.get_path_single_goal(&mut pathing_grid, *start, *end)); - } - }) - }); + for pruning in arr { + let improved_str = if pruning { " (improved pruning)" } else { "" }; + dao_bench_solver( + c, + format!("JPS{improved_str}").as_str(), + |pathing_grid: &mut PathingGrid| { + let mut solver = JPSSolver::new(&pathing_grid, pruning); + solver.initialize(&pathing_grid); + solver + }, + ); } } -fn dao_bench_astar(c: &mut Criterion) { - let bench_set = ["dao/arena", "dao/den009d", "dao/den312d"]; - for name in bench_set { - let (bool_grid, scenarios) = get_benchmark(name.to_owned()); - let mut pathing_grid: PathingGrid = - PathingGrid::new(bool_grid.width, bool_grid.height, true); - pathing_grid.grid = bool_grid.clone(); - pathing_grid.generate_components(); - let solver = AstarSolver::new(); - let diag_str = if ALLOW_DIAGONAL { "8-grid" } else { "4-grid" }; +fn dao_bench_alt(c: &mut Criterion) { + dao_bench_solver( + c, + "ALT (MC)", + |pathing_grid: &mut PathingGrid| { + let landmarks = generate_landmarks_mc(&pathing_grid, 64); + ALTSolver::new(landmarks, pathing_grid) + }, + ); +} - c.bench_function(format!("{name}, Astar {diag_str}").as_str(), |b| { - b.iter(|| { - for (start, end, _) in &scenarios { - black_box(solver.get_path_single_goal(&mut pathing_grid, *start, *end)); - } - }) - }); - } +fn dao_bench_astar(c: &mut Criterion) { + dao_bench_solver(c, "Astar", |_: &mut PathingGrid| { + AstarSolver::new() + }); } -fn dao_bench_dijkstra(c: &mut Criterion) { - let bench_set = ["dao/arena", "dao/den009d", "dao/den312d"]; - for name in bench_set { - let (bool_grid, scenarios) = get_benchmark(name.to_owned()); - let mut pathing_grid: PathingGrid = - PathingGrid::new(bool_grid.width, bool_grid.height, true); - pathing_grid.grid = bool_grid.clone(); - pathing_grid.generate_components(); - let solver = DijkstraSolver; - let diag_str = if ALLOW_DIAGONAL { "8-grid" } else { "4-grid" }; - c.bench_function(format!("{name}, Dijkstra {diag_str}").as_str(), |b| { - b.iter(|| { - for (start, end, _) in &scenarios { - black_box(solver.get_path_single_goal(&mut pathing_grid, *start, *end)); - } - }) - }); - } +fn dao_bench_dijkstra(c: &mut Criterion) { + dao_bench_solver(c, "Dijkstra", |_: &mut PathingGrid| { + DijkstraSolver + }); } criterion_group!( benches, // dao_bench, - dao_bench, + // dao_bench, // dao_bench_jps, - // dao_bench_jps, - dao_bench_alt, - // dao_bench_astar, + dao_bench_jps, + dao_bench_alt, + dao_bench_astar, // dao_bench_astar, // dao_bench_dijkstra, // dao_bench_dijkstra From 404043605c863a7489455f868dff1a2bfe0c902e Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Sun, 23 Nov 2025 20:44:17 +0100 Subject: [PATCH 05/16] Changed successors, heuristic and success in astar_jps from FnMut to Fn as in practice they have always been Fn --- src/astar_jps.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/astar_jps.rs b/src/astar_jps.rs index 47adaf9..93d83d0 100644 --- a/src/astar_jps.rs +++ b/src/astar_jps.rs @@ -120,15 +120,15 @@ where pub fn astar_jps( &mut self, start: &N, - mut successors: FN, - mut heuristic: FH, - mut success: FS, + successors: FN, + heuristic: FH, + success: FS, ) -> Option<(Vec, C)> where - FN: FnMut(&Option<&N>, &N) -> IN, + FN: Fn(&Option<&N>, &N) -> IN, IN: IntoIterator, - FH: FnMut(&N) -> C, - FS: FnMut(&N) -> bool, + FH: Fn(&N) -> C, + FS: Fn(&N) -> bool, { self.fringe.clear(); self.parents.clear(); @@ -199,10 +199,10 @@ pub fn astar_jps( where N: Eq + Hash + Clone, C: Zero + Ord + Copy + Hash, - FN: FnMut(&Option<&N>, &N) -> IN, + FN: Fn(&Option<&N>, &N) -> IN, IN: IntoIterator, - FH: FnMut(&N) -> C, - FS: FnMut(&N) -> bool, + FH: Fn(&N) -> C, + FS: Fn(&N) -> bool, { let mut search = DefaultSearchContext::new(); search.astar_jps(start, successors, heuristic, success) From 01d1c4d76102f9fceb78c64fffd40da9cd774ce9 Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Sun, 23 Nov 2025 21:23:42 +0100 Subject: [PATCH 06/16] Implemented greedy landmark selection algorithm from single initial landmark --- benches/comparison_bench.rs | 28 +++++++++++++------ src/solver/alt.rs | 55 +++++++++++++++++++++++++++++-------- 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/benches/comparison_bench.rs b/benches/comparison_bench.rs index 0a30646..9cb62d4 100644 --- a/benches/comparison_bench.rs +++ b/benches/comparison_bench.rs @@ -14,7 +14,7 @@ use std::hint::black_box; fn dao_bench(c: &mut Criterion) { let arr: SmallVec<[bool; 2]> = if ALLOW_DIAGONAL { - smallvec![false, true] + smallvec![false] } else { smallvec![false] }; @@ -97,7 +97,7 @@ pub fn generate_landmarks_mc( fn dao_bench_jps(c: &mut Criterion) { let arr: SmallVec<[bool; 2]> = if ALLOW_DIAGONAL { - smallvec![false, true] + smallvec![false] } else { smallvec![false] }; @@ -115,17 +115,26 @@ fn dao_bench_jps(c: &mut Criterion) { } } -fn dao_bench_alt(c: &mut Criterion) { +fn dao_bench_alt_mc(c: &mut Criterion) { dao_bench_solver( c, "ALT (MC)", |pathing_grid: &mut PathingGrid| { let landmarks = generate_landmarks_mc(&pathing_grid, 64); - ALTSolver::new(landmarks, pathing_grid) + ALTSolver::new_from_landmarks(landmarks, pathing_grid) + }, + ); +} +fn dao_bench_alt_greedy(c: &mut Criterion) { + dao_bench_solver( + c, + "ALT (greedy)", + |pathing_grid: &mut PathingGrid| { + let landmarks = generate_landmarks_mc(&pathing_grid, 1); + ALTSolver::new_greedy(landmarks[0], 64, pathing_grid) }, ); } - fn dao_bench_astar(c: &mut Criterion) { dao_bench_solver(c, "Astar", |_: &mut PathingGrid| { AstarSolver::new() @@ -141,11 +150,12 @@ fn dao_bench_dijkstra(c: &mut Criterion) { criterion_group!( benches, // dao_bench, - // dao_bench, + dao_bench, // dao_bench_jps, - dao_bench_jps, - dao_bench_alt, - dao_bench_astar, + dao_bench_jps, + dao_bench_alt_greedy, + dao_bench_alt_mc, + // dao_bench_astar, // dao_bench_astar, // dao_bench_dijkstra, // dao_bench_dijkstra diff --git a/src/solver/alt.rs b/src/solver/alt.rs index 23469c8..2f2298b 100644 --- a/src/solver/alt.rs +++ b/src/solver/alt.rs @@ -2,7 +2,7 @@ use std::i32; use crate::solver::dijkstra::DijkstraSolver; use crate::{pathing_grid::PathingGrid, solver::GridSolver, N_SMALLVEC_SIZE}; -use grid_util::{Grid, Point, SimpleGrid, SimpleValueGrid, ValueGrid}; +use grid_util::{Grid, Point, SimpleGrid, ValueGrid}; use smallvec::SmallVec; use std::iter::zip; @@ -12,37 +12,68 @@ pub struct ALTSolver { landmark_distances: SimpleGrid>, } impl ALTSolver { - pub fn new( + pub fn new_from_landmarks( landmarks: Vec, grid: &mut PathingGrid, ) -> ALTSolver { let d = DijkstraSolver; - let mut landmark_distances_flipped = Vec::new(); + let mut landmark_distances = SimpleGrid::new(grid.width(), grid.height(), SmallVec::new()); for landmark in &landmarks { - let mut dists = SimpleValueGrid::new(grid.width(), grid.height(), i32::MAX); let mut ct = grid.context.lock().unwrap(); ct.astar_jps( &landmark, |parent, node| d.successors(grid, *parent, node, &|_| false), |_| 0, |_| false, - ) - .map(|(v, _c)| v); + ); for (p, (_, c)) in &ct.parents { - dists.set_point(*p, *c); + landmark_distances.get_point_mut(*p).unwrap().push(*c); } - landmark_distances_flipped.push(dists); } + ALTSolver { + landmarks, + landmark_distances, + } + } + pub fn new_greedy( + initial_landmark: Point, + landmark_count: usize, + grid: &mut PathingGrid, + ) -> ALTSolver { + let d = DijkstraSolver; + let mut landmarks = vec![initial_landmark]; let mut landmark_distances = SimpleGrid::new(grid.width(), grid.height(), SmallVec::new()); - for grid in landmark_distances_flipped { + while landmarks.len() < landmark_count { + let landmark = landmarks.last().unwrap(); + let mut ct = grid.context.lock().unwrap(); + ct.astar_jps( + &landmark, + |parent, node| d.successors(grid, *parent, node, &|_| false), + |_| 0, + |_| false, + ); + for (p, (_, c)) in &ct.parents { + landmark_distances.get_point_mut(*p).unwrap().push(*c); + } + let mut max_point = *landmark; + let mut max_cost = 0; for x in 0..grid.width() as i32 { for y in 0..grid.height() as i32 { - landmark_distances - .get_mut(x, y) + // Look to maximize the minimal distance to any existing landmark for a greedy choice + let min_c = *landmark_distances + .get(x, y) .unwrap() - .push(grid.get(x, y)); + .iter() + .min() + .unwrap_or(&0); + if min_c > max_cost { + max_point = Point::new(x, y); + max_cost = min_c; + } } } + // println!("Adding new point greedily: {max_point:?}, cost: {max_cost}"); + landmarks.push(max_point); } ALTSolver { landmarks, From 754d6f64336cdc366af7951dc14509f6dd7863ee Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Sun, 23 Nov 2025 22:23:58 +0100 Subject: [PATCH 07/16] Simplified approximate goal condition using D instead of repeated EQUAL_EDGE_COST if --- src/lib.rs | 4 ++-- src/solver/mod.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1246142..1f445ad 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -516,14 +516,14 @@ impl Pathfinder { |parent, node| { if GRAPH_PRUNING { self.jps_neighbours(*parent, node, &|node_pos| { - self.heuristic(node_pos, &goal) <= if EQUAL_EDGE_COST { 1 } else { 99 } + self.heuristic(node_pos, &goal) <= D }) } else { self.neighborhood_points_and_cost(node) } }, |point| (self.heuristic(point, &goal) as f32 * self.heuristic_factor) as i32, - |point| self.heuristic(point, &goal) <= if EQUAL_EDGE_COST { 1 } else { 99 }, + |point| self.heuristic(point, &goal) <= D, ) .map(|(v, _c)| v) } diff --git a/src/solver/mod.rs b/src/solver/mod.rs index 0d33bec..e596dec 100644 --- a/src/solver/mod.rs +++ b/src/solver/mod.rs @@ -1,4 +1,4 @@ -use crate::{pathing_grid::PathingGrid, waypoints_to_path, C, D, E, EQUAL_EDGE_COST}; +use crate::{pathing_grid::PathingGrid, waypoints_to_path, C, D, E}; use grid_util::Point; pub mod alt; @@ -131,11 +131,11 @@ pub trait GridSolver { &start, |parent, node| { self.successors(grid, *parent, node, &|node_pos| { - self.heuristic(grid, node_pos, &goal) <= if EQUAL_EDGE_COST { 1 } else { 99 } + self.heuristic(grid, node_pos, &goal) <= D }) }, |point| self.heuristic(grid, point, &goal), - |point| self.heuristic(grid, point, &goal) <= if EQUAL_EDGE_COST { 1 } else { 99 }, + |point| self.heuristic(grid, point, &goal) <= D, ) .map(|(v, _c)| v) } From d1422a1f06f9145f6dd4fcd4e627def999834632 Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Tue, 25 Nov 2025 20:15:11 +0100 Subject: [PATCH 08/16] Improved tightness of heuristic by adding missing base heuristic of straight line distance --- src/solver/alt.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/solver/alt.rs b/src/solver/alt.rs index 2f2298b..8432b67 100644 --- a/src/solver/alt.rs +++ b/src/solver/alt.rs @@ -100,12 +100,16 @@ impl GridSolver for ALTSolver { fn heuristic( &self, - _: &PathingGrid, + grid: &PathingGrid, p1: &Point, p2: &Point, ) -> i32 { let g1 = self.landmark_distances.get_point(*p1).unwrap(); let g2 = self.landmark_distances.get_point(*p2).unwrap(); - zip(g1, g2).map(|(x, y)| (x - y).abs()).max().unwrap() + zip(g1, g2) + .map(|(x, y)| (x - y).abs()) + .max() + .unwrap() + .max(self.cost(grid, p1, p2)) } } From c4372a7af45c4b42b2c8f0c137a385841a715714 Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Wed, 26 Nov 2025 19:57:16 +0100 Subject: [PATCH 09/16] Removed grid reference parameter from heuristic and cost, simplifying solver trait and corresponding solvers --- src/lib.rs | 2 +- src/solver/alt.rs | 9 ++------- src/solver/astar.rs | 9 ++------- src/solver/dijkstra.rs | 7 +------ src/solver/jps.rs | 9 ++------- src/solver/mod.rs | 26 ++++++++------------------ 6 files changed, 16 insertions(+), 46 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1f445ad..f150dc9 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ use std::sync::{Arc, Mutex}; use crate::astar_jps::DefaultSearchContext; -pub const ALLOW_CORNER_CUTTING: bool = false; +pub const ALLOW_CORNER_CUTTING: bool = true; const EQUAL_EDGE_COST: bool = false; const GRAPH_PRUNING: bool = true; const N_SMALLVEC_SIZE: usize = 8; diff --git a/src/solver/alt.rs b/src/solver/alt.rs index 8432b67..1090b62 100644 --- a/src/solver/alt.rs +++ b/src/solver/alt.rs @@ -98,18 +98,13 @@ impl GridSolver for ALTSolver { grid.neighborhood_points_and_cost(node) } - fn heuristic( - &self, - grid: &PathingGrid, - p1: &Point, - p2: &Point, - ) -> i32 { + fn heuristic(&self, p1: &Point, p2: &Point) -> i32 { let g1 = self.landmark_distances.get_point(*p1).unwrap(); let g2 = self.landmark_distances.get_point(*p2).unwrap(); zip(g1, g2) .map(|(x, y)| (x - y).abs()) .max() .unwrap() - .max(self.cost(grid, p1, p2)) + .max(self.cost::(p1, p2)) } } diff --git a/src/solver/astar.rs b/src/solver/astar.rs index b926c74..17a857d 100644 --- a/src/solver/astar.rs +++ b/src/solver/astar.rs @@ -33,13 +33,8 @@ impl GridSolver for AstarSolver { } /// Just the normal cost times a heuristic factor. - fn heuristic( - &self, - grid: &PathingGrid, - p1: &Point, - p2: &Point, - ) -> i32 { - (self.cost(grid, p1, p2) as f32 * self.heuristic_factor) as i32 + fn heuristic(&self, p1: &Point, p2: &Point) -> i32 { + (self.cost::(p1, p2) as f32 * self.heuristic_factor) as i32 } } diff --git a/src/solver/dijkstra.rs b/src/solver/dijkstra.rs index a45a3e3..f631742 100644 --- a/src/solver/dijkstra.rs +++ b/src/solver/dijkstra.rs @@ -23,12 +23,7 @@ impl GridSolver for DijkstraSolver { } /// Just the cost times a heuristic factor. - fn heuristic( - &self, - _: &PathingGrid, - _: &Point, - _: &Point, - ) -> i32 { + fn heuristic(&self, _: &Point, _: &Point) -> i32 { 0 } } diff --git a/src/solver/jps.rs b/src/solver/jps.rs index 3fb7584..569407e 100644 --- a/src/solver/jps.rs +++ b/src/solver/jps.rs @@ -61,13 +61,8 @@ impl GridSolver for JPSSolver { } /// Uses C as cost for cardinal (straight) moves and D for diagonal moves. - fn heuristic( - &self, - grid: &PathingGrid, - p1: &Point, - p2: &Point, - ) -> i32 { - self.cost(grid, p1, p2) + fn heuristic(&self, p1: &Point, p2: &Point) -> i32 { + self.cost::(p1, p2) } } impl JPSSolver { diff --git a/src/solver/mod.rs b/src/solver/mod.rs index e596dec..fe666b8 100644 --- a/src/solver/mod.rs +++ b/src/solver/mod.rs @@ -14,20 +14,10 @@ pub fn convert_cost_to_unit_cost_float(cost: i32) -> f64 { pub trait GridSolver { type Successors: IntoIterator; - fn heuristic( - &self, - grid: &PathingGrid, - p1: &Point, - p2: &Point, - ) -> i32; + fn heuristic(&self, p1: &Point, p2: &Point) -> i32; /// Uses C as cost for cardinal (straight) moves and D for diagonal moves. - fn cost( - &self, - grid: &PathingGrid, - p1: &Point, - p2: &Point, - ) -> i32 { + fn cost(&self, p1: &Point, p2: &Point) -> i32 { if ALLOW_DIAGONAL { let delta_x = (p1.x - p2.x).abs(); let delta_y = (p1.y - p2.y).abs(); @@ -61,7 +51,7 @@ pub trait GridSolver { for i in 1..n { let v_old = v; v = path[i]; - let cost = self.cost(&pathing_grid, &v_old, &v); + let cost = self.cost::(&v_old, &v); total_cost_int += cost; } total_cost_int @@ -107,7 +97,7 @@ pub trait GridSolver { ct.astar_jps( &start, |parent, node| self.successors(grid, *parent, node, &|node_pos| *node_pos == goal), - |point| self.heuristic(grid, point, &goal), + |point| self.heuristic::(point, &goal), |point| *point == goal, ) .map(|(v, _c)| v) @@ -131,11 +121,11 @@ pub trait GridSolver { &start, |parent, node| { self.successors(grid, *parent, node, &|node_pos| { - self.heuristic(grid, node_pos, &goal) <= D + self.heuristic::(node_pos, &goal) <= D }) }, - |point| self.heuristic(grid, point, &goal), - |point| self.heuristic(grid, point, &goal) <= D, + |point| self.heuristic::(point, &goal), + |point| self.heuristic::(point, &goal) <= D, ) .map(|(v, _c)| v) } @@ -166,7 +156,7 @@ pub trait GridSolver { |point| { goals .iter() - .map(|x| self.heuristic(grid, point, x)) + .map(|x| self.heuristic::(point, x)) .min() .unwrap() }, From b429215fa2887a24e24d07cd2f18bef268fc560f Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Fri, 28 Nov 2025 15:36:08 +0100 Subject: [PATCH 10/16] Removed criterion 0.4 from grid_pathfinding_benchmark dependencies and ran cargo update --- Cargo.lock | 227 +++++--------------------- grid_pathfinding_benchmark/Cargo.toml | 1 - 2 files changed, 39 insertions(+), 189 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21ae8a9..da87522 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,17 +23,6 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.5.0" @@ -46,12 +35,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.10.0" @@ -114,42 +97,21 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" -dependencies = [ - "bitflags 1.3.2", - "clap_lex 0.2.4", - "indexmap 1.9.3", - "textwrap", -] - -[[package]] -name = "clap" -version = "4.5.51" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.51" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstyle", - "clap_lex 0.7.6", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", + "clap_lex", ] [[package]] @@ -158,32 +120,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" -[[package]] -name = "criterion" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" -dependencies = [ - "anes", - "atty", - "cast", - "ciborium", - "clap 3.2.25", - "criterion-plot 0.5.0", - "itertools 0.10.5", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - [[package]] name = "criterion" version = "0.7.0" @@ -193,9 +129,9 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.51", - "criterion-plot 0.6.0", - "itertools 0.13.0", + "clap", + "criterion-plot", + "itertools", "num-traits", "oorandom", "plotters", @@ -207,16 +143,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools 0.10.5", -] - [[package]] name = "criterion-plot" version = "0.6.0" @@ -224,7 +150,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" dependencies = [ "cast", - "itertools 0.13.0", + "itertools", ] [[package]] @@ -339,11 +265,11 @@ dependencies = [ name = "grid_pathfinding" version = "0.2.1" dependencies = [ - "criterion 0.7.0", + "criterion", "fxhash", "grid_pathfinding_benchmark", "grid_util", - "indexmap 2.12.0", + "indexmap", "log", "num-traits", "petgraph", @@ -355,7 +281,6 @@ dependencies = [ name = "grid_pathfinding_benchmark" version = "0.1.0" dependencies = [ - "criterion 0.4.0", "csv", "grid_util", "serde", @@ -389,12 +314,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.15.5" @@ -406,9 +325,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heck" @@ -416,42 +335,14 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "indexmap" -version = "1.9.3" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.16.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", + "hashbrown 0.16.1", ] [[package]] @@ -471,20 +362,14 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "libc" version = "0.2.177" @@ -546,12 +431,6 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" -[[package]] -name = "os_str_bytes" -version = "6.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" - [[package]] name = "petgraph" version = "0.8.3" @@ -560,7 +439,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", "hashbrown 0.15.5", - "indexmap 2.12.0", + "indexmap", "serde", ] @@ -771,7 +650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db09040cc89e461f1a265139777a2bde7f8d8c67c4936f700c63ce3e2904d468" dependencies = [ "base64", - "bitflags 2.10.0", + "bitflags", "serde", "serde_derive", "unicode-ident", @@ -867,21 +746,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.109" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "textwrap" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" - [[package]] name = "tinytemplate" version = "1.2.1" @@ -907,7 +780,7 @@ version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ - "indexmap 2.12.0", + "indexmap", "toml_datetime", "toml_parser", "winnow", @@ -955,9 +828,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -968,9 +841,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -978,9 +851,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", "proc-macro2", @@ -991,39 +864,23 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" version = "0.1.11" @@ -1033,12 +890,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-link" version = "0.2.1" @@ -1056,9 +907,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -1071,18 +922,18 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" dependencies = [ "proc-macro2", "quote", diff --git a/grid_pathfinding_benchmark/Cargo.toml b/grid_pathfinding_benchmark/Cargo.toml index 1a13c14..4808a44 100644 --- a/grid_pathfinding_benchmark/Cargo.toml +++ b/grid_pathfinding_benchmark/Cargo.toml @@ -13,7 +13,6 @@ readme = "README.md" [dependencies] grid_util = "0.2" -criterion = { version = "0.4", features = ["html_reports"] } csv = "1.3.0" serde = "1.0.204" walkdir = "2.5.0" From ee3b2def26ae842d7bc45a941f50edcec1d9b78d Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Fri, 28 Nov 2025 15:51:32 +0100 Subject: [PATCH 11/16] Created solver::landmarks submodule, cherry-picking this change from alt-max-determinant --- src/solver/alt.rs | 76 ++++-------------------------- src/solver/landmarks.rs | 102 ++++++++++++++++++++++++++++++++++++++++ src/solver/mod.rs | 1 + 3 files changed, 113 insertions(+), 66 deletions(-) create mode 100644 src/solver/landmarks.rs diff --git a/src/solver/alt.rs b/src/solver/alt.rs index 1090b62..0c8ea79 100644 --- a/src/solver/alt.rs +++ b/src/solver/alt.rs @@ -1,83 +1,31 @@ -use std::i32; - -use crate::solver::dijkstra::DijkstraSolver; +use crate::solver::landmarks::LandmarkMap; use crate::{pathing_grid::PathingGrid, solver::GridSolver, N_SMALLVEC_SIZE}; -use grid_util::{Grid, Point, SimpleGrid, ValueGrid}; +use grid_util::Point; use smallvec::SmallVec; -use std::iter::zip; #[derive(Clone, Debug)] pub struct ALTSolver { - landmarks: Vec, - landmark_distances: SimpleGrid>, + landmark_map: LandmarkMap, } impl ALTSolver { pub fn new_from_landmarks( landmarks: Vec, grid: &mut PathingGrid, ) -> ALTSolver { - let d = DijkstraSolver; - let mut landmark_distances = SimpleGrid::new(grid.width(), grid.height(), SmallVec::new()); - for landmark in &landmarks { - let mut ct = grid.context.lock().unwrap(); - ct.astar_jps( - &landmark, - |parent, node| d.successors(grid, *parent, node, &|_| false), - |_| 0, - |_| false, - ); - for (p, (_, c)) in &ct.parents { - landmark_distances.get_point_mut(*p).unwrap().push(*c); - } - } ALTSolver { - landmarks, - landmark_distances, + landmark_map: LandmarkMap::new_from_landmarks(landmarks, grid), } } + pub fn new_from_landmark_map(landmark_map: LandmarkMap) -> ALTSolver { + ALTSolver { landmark_map } + } pub fn new_greedy( initial_landmark: Point, landmark_count: usize, grid: &mut PathingGrid, ) -> ALTSolver { - let d = DijkstraSolver; - let mut landmarks = vec![initial_landmark]; - let mut landmark_distances = SimpleGrid::new(grid.width(), grid.height(), SmallVec::new()); - while landmarks.len() < landmark_count { - let landmark = landmarks.last().unwrap(); - let mut ct = grid.context.lock().unwrap(); - ct.astar_jps( - &landmark, - |parent, node| d.successors(grid, *parent, node, &|_| false), - |_| 0, - |_| false, - ); - for (p, (_, c)) in &ct.parents { - landmark_distances.get_point_mut(*p).unwrap().push(*c); - } - let mut max_point = *landmark; - let mut max_cost = 0; - for x in 0..grid.width() as i32 { - for y in 0..grid.height() as i32 { - // Look to maximize the minimal distance to any existing landmark for a greedy choice - let min_c = *landmark_distances - .get(x, y) - .unwrap() - .iter() - .min() - .unwrap_or(&0); - if min_c > max_cost { - max_point = Point::new(x, y); - max_cost = min_c; - } - } - } - // println!("Adding new point greedily: {max_point:?}, cost: {max_cost}"); - landmarks.push(max_point); - } ALTSolver { - landmarks, - landmark_distances, + landmark_map: LandmarkMap::new_minmax_greedy(initial_landmark, landmark_count, grid), } } } @@ -99,12 +47,8 @@ impl GridSolver for ALTSolver { } fn heuristic(&self, p1: &Point, p2: &Point) -> i32 { - let g1 = self.landmark_distances.get_point(*p1).unwrap(); - let g2 = self.landmark_distances.get_point(*p2).unwrap(); - zip(g1, g2) - .map(|(x, y)| (x - y).abs()) - .max() - .unwrap() + self.landmark_map + .triangle_heuristic(p1, p2) .max(self.cost::(p1, p2)) } } diff --git a/src/solver/landmarks.rs b/src/solver/landmarks.rs new file mode 100644 index 0000000..90d9a68 --- /dev/null +++ b/src/solver/landmarks.rs @@ -0,0 +1,102 @@ +use core::panic; + +use crate::solver::dijkstra::DijkstraSolver; +use crate::{pathing_grid::PathingGrid, solver::GridSolver}; +use grid_util::{Grid, Point, SimpleGrid, ValueGrid}; +use smallvec::SmallVec; +use std::iter::zip; + +#[derive(Clone, Debug)] +pub struct LandmarkMap { + landmarks: Vec, + landmark_distances: SimpleGrid>, +} +impl LandmarkMap { + pub fn len(&self) -> usize { + self.landmarks.len() + } + #[inline(always)] + pub fn triangle_heuristic(&self, p1: &Point, p2: &Point) -> i32 { + let g1 = self.landmark_distances.get_point(*p1).unwrap(); + let g2 = self.landmark_distances.get_point(*p2).unwrap(); + zip(g1, g2).map(|(x, y)| (x - y).abs()).max().unwrap() + } + + pub fn new_from_landmarks( + landmarks: Vec, + grid: &mut PathingGrid, + ) -> LandmarkMap { + let d = DijkstraSolver; + let mut landmark_distances = SimpleGrid::new(grid.width(), grid.height(), SmallVec::new()); + for landmark in &landmarks { + let mut ct = grid.context.lock().unwrap(); + ct.astar_jps( + &landmark, + |parent, node| d.successors(grid, *parent, node, &|_| false), + |_| 0, + |_| false, + ); + for (p, (_, c)) in &ct.parents { + landmark_distances.get_point_mut(*p).unwrap().push(*c); + } + } + LandmarkMap { + landmarks, + landmark_distances, + } + } + pub fn new_minmax_greedy( + initial_landmark: Point, + landmark_count: usize, + grid: &mut PathingGrid, + ) -> LandmarkMap { + let d = DijkstraSolver; + let mut landmarks = vec![]; + let mut landmark_distances = SimpleGrid::new(grid.width(), grid.height(), SmallVec::new()); + + // Look to maximize the minimal distance to any existing landmark for a greedy choice + while landmarks.len() < landmark_count { + if landmarks.len() > 0 { + let mut max_point = None; + let mut max_cost = 0; + for x in 0..grid.width() as i32 { + for y in 0..grid.height() as i32 { + let min_c = *landmark_distances + .get(x, y) + .unwrap() + .iter() + .min() + .unwrap_or(&0); + if min_c > max_cost { + max_point = Some(Point::new(x, y)); + max_cost = min_c; + } + } + } + match max_point { + Some(p) => landmarks.push(p), + None => { + panic!("No point that is maximally far away from existing ones was found") + } + } + } else { + landmarks.push(initial_landmark); + } + let landmark = landmarks.last().unwrap(); + let mut ct = grid.context.lock().unwrap(); + ct.astar_jps( + &landmark, + |parent, node| d.successors(grid, *parent, node, &|_| false), + |_| 0, + |_| false, + ); + for (p, (_, c)) in &ct.parents { + landmark_distances.get_point_mut(*p).unwrap().push(*c); + } + } + LandmarkMap { + landmarks, + landmark_distances, + } + } +} diff --git a/src/solver/mod.rs b/src/solver/mod.rs index fe666b8..47d74ed 100644 --- a/src/solver/mod.rs +++ b/src/solver/mod.rs @@ -5,6 +5,7 @@ pub mod alt; pub mod astar; pub mod dijkstra; pub mod jps; +pub mod landmarks; /// Converts the integer cost to an approximate floating point equivalent where cardinal directions have cost 1.0. pub fn convert_cost_to_unit_cost_float(cost: i32) -> f64 { From 3c1c399dbb2178e9d8ef95b98419560f0e707724 Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Fri, 28 Nov 2025 15:55:44 +0100 Subject: [PATCH 12/16] Cleaned up comparison_bench, comparing just arena2 between solvers as most representative single test --- benches/comparison_bench.rs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/benches/comparison_bench.rs b/benches/comparison_bench.rs index 9cb62d4..26a51fa 100644 --- a/benches/comparison_bench.rs +++ b/benches/comparison_bench.rs @@ -19,9 +19,9 @@ fn dao_bench(c: &mut Criterion) { smallvec![false] }; let bench_set = if ALLOW_DIAGONAL { - ["dao/arena", "dao/den312d", "dao/arena2"] + ["dao/arena2"] } else { - ["dao/arena", "dao/den009d", "dao/den312d"] + ["dao/arena2"] }; for pruning in arr { for name in bench_set { @@ -55,9 +55,9 @@ fn dao_bench_solver( FS: Fn(&mut PathingGrid) -> S, { let bench_set = if ALLOW_DIAGONAL { - ["dao/arena", "dao/den312d", "dao/arena2"] + ["dao/arena2"] } else { - ["dao/arena", "dao/den009d", "dao/den312d"] + ["dao/arena2"] }; for name in bench_set { let (bool_grid, scenarios) = get_benchmark(name.to_owned()); @@ -115,12 +115,13 @@ fn dao_bench_jps(c: &mut Criterion) { } } +const N_LANDMARKS: usize = 64; fn dao_bench_alt_mc(c: &mut Criterion) { dao_bench_solver( c, "ALT (MC)", |pathing_grid: &mut PathingGrid| { - let landmarks = generate_landmarks_mc(&pathing_grid, 64); + let landmarks = generate_landmarks_mc(&pathing_grid, N_LANDMARKS); ALTSolver::new_from_landmarks(landmarks, pathing_grid) }, ); @@ -131,7 +132,7 @@ fn dao_bench_alt_greedy(c: &mut Criterion) { "ALT (greedy)", |pathing_grid: &mut PathingGrid| { let landmarks = generate_landmarks_mc(&pathing_grid, 1); - ALTSolver::new_greedy(landmarks[0], 64, pathing_grid) + ALTSolver::new_greedy(landmarks[0], N_LANDMARKS, pathing_grid) }, ); } @@ -149,15 +150,9 @@ fn dao_bench_dijkstra(c: &mut Criterion) { criterion_group!( benches, - // dao_bench, dao_bench, - // dao_bench_jps, dao_bench_jps, dao_bench_alt_greedy, dao_bench_alt_mc, - // dao_bench_astar, - // dao_bench_astar, - // dao_bench_dijkstra, - // dao_bench_dijkstra ); criterion_main!(benches); From a0452b0230e22282e2a24988337c42438d29cb40 Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Fri, 28 Nov 2025 15:59:16 +0100 Subject: [PATCH 13/16] Removed pathing_grid parameter from get_path_cost and get_path_cost_float --- src/solver/mod.rs | 14 +++----------- tests/benchmark_distances.rs | 4 ++-- tests/fuzz_test.rs | 10 +++++----- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/solver/mod.rs b/src/solver/mod.rs index 47d74ed..9b94804 100644 --- a/src/solver/mod.rs +++ b/src/solver/mod.rs @@ -41,11 +41,7 @@ pub trait GridSolver { where F: Fn(&Point) -> bool; - fn get_path_cost( - &self, - path: &Vec, - pathing_grid: &PathingGrid, - ) -> i32 { + fn get_path_cost(&self, path: &Vec) -> i32 { let mut v = path[0]; let n = path.len(); let mut total_cost_int = 0; @@ -57,12 +53,8 @@ pub trait GridSolver { } total_cost_int } - fn get_path_cost_float( - &self, - path: &Vec, - pathing_grid: &PathingGrid, - ) -> f64 { - convert_cost_to_unit_cost_float(self.get_path_cost(path, pathing_grid)) + fn get_path_cost_float(&self, path: &Vec) -> f64 { + convert_cost_to_unit_cost_float(self.get_path_cost::(path)) } fn get_path_single_goal( &self, diff --git a/tests/benchmark_distances.rs b/tests/benchmark_distances.rs index 20a737a..183363e 100644 --- a/tests/benchmark_distances.rs +++ b/tests/benchmark_distances.rs @@ -33,7 +33,7 @@ fn verify_solution_distance_jps() { .get_path_single_goal(&mut pathing_grid, *start, *end) .unwrap(); save_path(path.clone(), "path.csv").unwrap(); - let float_cost = solver.get_path_cost_float(&path, &pathing_grid); + let float_cost = solver.get_path_cost_float::(&path); println!("My distance: {float_cost}"); if *distance >= 0.01 { let delta_dist = (float_cost - distance).abs() / distance; @@ -60,7 +60,7 @@ fn verify_solution_distance_astar() { .get_path_single_goal(&mut pathing_grid, *start, *end) .unwrap(); save_path(path.clone(), "path.csv").unwrap(); - let float_cost = solver.get_path_cost_float(&path, &pathing_grid); + let float_cost = solver.get_path_cost_float::(&path); println!("My distance: {float_cost}"); if *distance >= 0.01 { let delta_dist = (float_cost - distance).abs() / distance; diff --git a/tests/fuzz_test.rs b/tests/fuzz_test.rs index e657805..8c918f7 100644 --- a/tests/fuzz_test.rs +++ b/tests/fuzz_test.rs @@ -114,8 +114,8 @@ fn distance_fuzzer() { .get_path_single_goal(&mut random_grid, start, end) .unwrap(); - let astar_cost = astar_solver.get_path_cost_float(&astar_path, &random_grid); - let jps_cost = jps_solver.get_path_cost_float(&jps_path, &random_grid); + let astar_cost = astar_solver.get_path_cost_float::(&astar_path); + let jps_cost = jps_solver.get_path_cost_float::(&jps_path); if astar_cost >= tolerance { let delta_dist = (jps_cost - astar_cost).abs() / astar_cost; if delta_dist >= tolerance { @@ -126,12 +126,12 @@ fn distance_fuzzer() { for (idx, &p) in jps_path.iter().enumerate().rev() { let jps_suffix = &jps_path[idx..].to_vec(); let jps_suffix_cost = - jps_solver.get_path_cost_float(jps_suffix, &random_grid); + jps_solver.get_path_cost_float::(jps_suffix); let astar_suffix_path = astar_solver .get_path_single_goal(&mut random_grid, p, end) .expect("A* should find a path from intermediate JPS node"); - let astar_suffix_cost = - astar_solver.get_path_cost_float(&astar_suffix_path, &random_grid); + let astar_suffix_cost = astar_solver + .get_path_cost_float::(&astar_suffix_path); let rel_diff = (jps_suffix_cost - astar_suffix_cost).abs() / astar_suffix_cost.max(1e-6); From de7ad00bf3e16f9c4b70f337f867ff8f8c1d7f4d Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Sun, 30 Nov 2025 16:25:15 +0100 Subject: [PATCH 14/16] Made reachable_fuzzer generic over GridSolver's --- tests/fuzz_test.rs | 71 +++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/tests/fuzz_test.rs b/tests/fuzz_test.rs index 8c918f7..bd437b9 100644 --- a/tests/fuzz_test.rs +++ b/tests/fuzz_test.rs @@ -47,36 +47,32 @@ fn visualize_grid( } } -fn reachable_fuzzer() { +fn reachable_fuzzer(create_solver: FS) +where + S: GridSolver, + FS: Fn(&mut PathingGrid) -> S, +{ const N: usize = 10; const N_GRIDS: usize = 10000; let mut rng = StdRng::seed_from_u64(0); - let arr: SmallVec<[bool; 2]> = if ALLOW_DIAGONAL { - smallvec![false, true] - } else { - smallvec![false] - }; - for improved_pruning in arr { - let mut random_grids: Vec> = Vec::new(); - for _ in 0..N_GRIDS { - random_grids.push(random_grid(N, N, &mut rng)) - } + let mut random_grids: Vec> = Vec::new(); + for _ in 0..N_GRIDS { + random_grids.push(random_grid(N, N, &mut rng)) + } - let start = Point::new(0, 0); - let end = Point::new(N as i32 - 1, N as i32 - 1); - for mut random_grid in random_grids { - let mut solver = JPSSolver::new(&random_grid, improved_pruning); - random_grid.set_point(start, false); - random_grid.set_point(end, false); - solver.initialize(&random_grid); - let reachable = random_grid.reachable(&start, &end); - let path = solver.get_path_single_goal(&mut random_grid, start, end); - // Show the grid if a path is not found - if path.is_some() != reachable { - visualize_grid(&random_grid, &start, &end); - } - assert!(path.is_some() == reachable); + let start = Point::new(0, 0); + let end = Point::new(N as i32 - 1, N as i32 - 1); + for mut random_grid in random_grids { + random_grid.set_point(start, false); + random_grid.set_point(end, false); + let solver = create_solver(&mut random_grid); + let reachable = random_grid.reachable(&start, &end); + let path = solver.get_path_single_goal(&mut random_grid, start, end); + // Show the grid if a path is not found + if path.is_some() != reachable { + visualize_grid(&random_grid, &start, &end); } + assert!(path.is_some() == reachable); } } @@ -158,14 +154,31 @@ fn distance_fuzzer() { } } +fn fuzz_reachable_jps_variants() { + let arr: SmallVec<[bool; 2]> = if ALLOW_DIAGONAL { + smallvec![false, true] + } else { + smallvec![false] + }; + for improved_pruning in arr { + reachable_fuzzer::( + |pathing_grid: &mut PathingGrid| { + let mut solver = JPSSolver::new(&pathing_grid, improved_pruning); + solver.initialize(&pathing_grid); + solver + }, + ) + } +} + #[test] -fn fuzz_reachable() { - reachable_fuzzer::() +fn fuzz_reachable_jps() { + fuzz_reachable_jps_variants::() } #[test] -fn fuzz_reachable_diagonal() { - reachable_fuzzer::() +fn fuzz_reachable_jps_diagonal() { + fuzz_reachable_jps_variants::() } #[test] From 61cfaf02fc62ee4a52e3abd9573b194409b44b5f Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Sun, 30 Nov 2025 16:34:20 +0100 Subject: [PATCH 15/16] Added reachability fuzz tests for ALT --- tests/fuzz_test.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/fuzz_test.rs b/tests/fuzz_test.rs index bd437b9..201a2ee 100644 --- a/tests/fuzz_test.rs +++ b/tests/fuzz_test.rs @@ -1,9 +1,7 @@ /// Fuzzes pathfinding system by checking for many random grids that a path is always found if the goal is reachable /// by being part of the same connected component. All system settings (diagonals, improved pruning) are tested. use grid_pathfinding::{ - pathing_grid::PathingGrid, - solver::{astar::AstarSolver, jps::JPSSolver, GridSolver}, - ALLOW_CORNER_CUTTING, + ALLOW_CORNER_CUTTING, pathing_grid::PathingGrid, solver::{GridSolver, alt::ALTSolver, astar::AstarSolver, jps::JPSSolver} }; use grid_util::*; use rand::prelude::*; @@ -162,7 +160,7 @@ fn fuzz_reachable_jps_variants() { }; for improved_pruning in arr { reachable_fuzzer::( - |pathing_grid: &mut PathingGrid| { + |pathing_grid| { let mut solver = JPSSolver::new(&pathing_grid, improved_pruning); solver.initialize(&pathing_grid); solver @@ -181,6 +179,20 @@ fn fuzz_reachable_jps_diagonal() { fuzz_reachable_jps_variants::() } +#[test] +fn fuzz_reachable_alt() { + reachable_fuzzer::( + |pathing_grid| { + ALTSolver::new_greedy(Point::new(0, 0), 2, pathing_grid) + }, + ); + reachable_fuzzer::( + |pathing_grid| { + ALTSolver::new_greedy(Point::new(0, 0), 2, pathing_grid) + }, + ) +} + #[test] fn fuzz_distance() { distance_fuzzer::() From 4f72d7f5a03158972ae9528035932a59dbd7ad9e Mon Sep 17 00:00:00 2001 From: Thom van der Woude Date: Sat, 3 Jan 2026 07:26:47 +0100 Subject: [PATCH 16/16] Made new_minmax_greedy handle the case where no new landmark is found --- src/solver/landmarks.rs | 5 ++++- tests/fuzz_test.rs | 32 ++++++++++++++------------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/solver/landmarks.rs b/src/solver/landmarks.rs index 90d9a68..b5a20dd 100644 --- a/src/solver/landmarks.rs +++ b/src/solver/landmarks.rs @@ -3,6 +3,7 @@ use core::panic; use crate::solver::dijkstra::DijkstraSolver; use crate::{pathing_grid::PathingGrid, solver::GridSolver}; use grid_util::{Grid, Point, SimpleGrid, ValueGrid}; +use log::warn; use smallvec::SmallVec; use std::iter::zip; @@ -76,7 +77,9 @@ impl LandmarkMap { match max_point { Some(p) => landmarks.push(p), None => { - panic!("No point that is maximally far away from existing ones was found") + let l = landmarks.len(); + warn!("No new landmark maximally far away from existing ones was found, returning only {l}/{landmark_count} landmarks"); + break; } } } else { diff --git a/tests/fuzz_test.rs b/tests/fuzz_test.rs index 201a2ee..81e1a46 100644 --- a/tests/fuzz_test.rs +++ b/tests/fuzz_test.rs @@ -1,7 +1,9 @@ /// Fuzzes pathfinding system by checking for many random grids that a path is always found if the goal is reachable /// by being part of the same connected component. All system settings (diagonals, improved pruning) are tested. use grid_pathfinding::{ - ALLOW_CORNER_CUTTING, pathing_grid::PathingGrid, solver::{GridSolver, alt::ALTSolver, astar::AstarSolver, jps::JPSSolver} + pathing_grid::PathingGrid, + solver::{alt::ALTSolver, astar::AstarSolver, jps::JPSSolver, GridSolver}, + ALLOW_CORNER_CUTTING, }; use grid_util::*; use rand::prelude::*; @@ -159,13 +161,11 @@ fn fuzz_reachable_jps_variants() { smallvec![false] }; for improved_pruning in arr { - reachable_fuzzer::( - |pathing_grid| { - let mut solver = JPSSolver::new(&pathing_grid, improved_pruning); - solver.initialize(&pathing_grid); - solver - }, - ) + reachable_fuzzer::(|pathing_grid| { + let mut solver = JPSSolver::new(&pathing_grid, improved_pruning); + solver.initialize(&pathing_grid); + solver + }) } } @@ -181,16 +181,12 @@ fn fuzz_reachable_jps_diagonal() { #[test] fn fuzz_reachable_alt() { - reachable_fuzzer::( - |pathing_grid| { - ALTSolver::new_greedy(Point::new(0, 0), 2, pathing_grid) - }, - ); - reachable_fuzzer::( - |pathing_grid| { - ALTSolver::new_greedy(Point::new(0, 0), 2, pathing_grid) - }, - ) + reachable_fuzzer::(|pathing_grid| { + ALTSolver::new_greedy(Point::new(0, 0), 2, pathing_grid) + }); + reachable_fuzzer::(|pathing_grid| { + ALTSolver::new_greedy(Point::new(0, 0), 2, pathing_grid) + }) } #[test]