Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
7ab951a
Made S ('swamp') on maps passable
tbvanderwoude Nov 8, 2025
8eb6b3c
Made load_benchmark also pass target distance for each point pair as …
tbvanderwoude Nov 8, 2025
0d6472a
Added verify_solution_distance test, currently failing presumably due…
tbvanderwoude Nov 8, 2025
cb60d19
Added ALLOW_CORNER_CUTTING as compile time constant with updated can_…
tbvanderwoude Nov 8, 2025
585d046
Added handling of distance 0 case in comparison and added saving of p…
tbvanderwoude Nov 8, 2025
42e9061
Integrated new move_to logic elsewhere, still passing all tests but b…
tbvanderwoude Nov 8, 2025
aebc8c8
Updated generate_components to respect new can_move_to rule, making f…
tbvanderwoude Nov 14, 2025
c9f286f
Renamed PathingGrid to Pathfinder
tbvanderwoude Nov 14, 2025
f4cc66b
Created new PathingGrid with just core functionalities like maintaini…
tbvanderwoude Nov 14, 2025
fb02531
Introduced new GridSolver trait with basic AstarSolver implementor
tbvanderwoude Nov 14, 2025
dd0cda5
Moved AstarSolver to new solver::astar submodule
tbvanderwoude Nov 14, 2025
c818251
Made PathingGrid::neighborhood_points and PathingGrid::indexed_neighb…
tbvanderwoude Nov 14, 2025
c0a209f
Renamed AstarContext to SearchContext
tbvanderwoude Nov 14, 2025
00c53af
Changed GridSolver::successors to take generic goal closure, adapting…
tbvanderwoude Nov 14, 2025
5b7aabb
Removed forced_mask from PathingGrid and made can_move_to, can_move_t…
tbvanderwoude Nov 14, 2025
61c5525
Added new solver::jps module with new JPSSolver, implementing the Gri…
tbvanderwoude Nov 14, 2025
8b14f2e
Added cost and default get_path_cost implementation to GridSolver
tbvanderwoude Nov 15, 2025
81c3daf
Added get_path_cost_float to GridSolver
tbvanderwoude Nov 15, 2025
c667ec5
Added fuzz test verifying Astar and JPS distances match
tbvanderwoude Nov 15, 2025
b5c64c0
Made JPSSolver complete and optimal by fixing pruned_neighborhood
tbvanderwoude Nov 15, 2025
4a5a01d
Ported old PathingGrid/Pathfinder units tests to new test suites for …
tbvanderwoude Nov 15, 2025
d168b56
Replaced gen_bool with random_bool in random_grid
tbvanderwoude Nov 15, 2025
9b85df5
Added WIP support for disabling corner cutting to original PathingGri…
tbvanderwoude Nov 15, 2025
5fc3b82
Moved neighbours grid and logic from PathingGrid to JPSSolver, furthe…
tbvanderwoude Nov 15, 2025
ce3cfe2
Expanded comparison bench to compare old JPS, new JPS and A*
tbvanderwoude Nov 15, 2025
b55fdbf
Fixed bug in forced_mask
tbvanderwoude Nov 15, 2025
4d1ee52
Gave cost a default implementation in GridSolver to reduce duplication
tbvanderwoude Nov 15, 2025
ebad550
Some final changes related to neighbours having moved to JPSSolver
tbvanderwoude Nov 15, 2025
81a5ea7
Added DijkstraSolver as A* with a zero heuristic
tbvanderwoude Nov 15, 2025
5f6f34f
Improved fuzz_distance, using A* to trace points where JPS paths subo…
tbvanderwoude Nov 15, 2025
60970d4
Introduced Frontier<C> trait, abstracting over frontier priority queu…
tbvanderwoude Nov 16, 2025
0482a52
Added priority_queue::PriorityQueue based Frontier implementation, ma…
tbvanderwoude Nov 16, 2025
f352c03
Removed priority_queue based PQSearchContext again as it appeared to …
tbvanderwoude Nov 16, 2025
1cf9ab7
Implemented simple bool const generic for toggling diagonals
tbvanderwoude Nov 16, 2025
5f106cb
Added dao_bench_dijkstra
tbvanderwoude Nov 16, 2025
3c53fc1
Merge branch 'benchmark-verification' into const-generic-diagonal
tbvanderwoude Nov 16, 2025
8ccab60
Added ALLOW_DIAGONAL const generic to Pathfinder also to keep compari…
tbvanderwoude Nov 16, 2025
47bf375
Removed useless 'improved pruning 4 grid' bench for new jps and chang…
tbvanderwoude Nov 16, 2025
17eb8d3
Removed grid argument from pruned_neighborhood in new JPS
tbvanderwoude Nov 16, 2025
8e8767e
Introduced get_path_single_goal_approximate and get_waypoints_single_…
tbvanderwoude Nov 23, 2025
3a56ba9
Merge branch 'main' into benchmark-verification
tbvanderwoude Nov 30, 2025
bf2847e
Cherry picked comparison_bench changes from altsolver
tbvanderwoude Nov 30, 2025
b914fa5
Removed generate_landmarks_mc
tbvanderwoude Nov 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 87 additions & 15 deletions benches/comparison_bench.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,110 @@
use criterion::{criterion_group, criterion_main, Criterion};
use grid_pathfinding::PathingGrid;
use grid_pathfinding::{
pathing_grid::PathingGrid,
solver::{astar::AstarSolver, dijkstra::DijkstraSolver, jps::JPSSolver, GridSolver},
Pathfinder,
};
use grid_pathfinding_benchmark::*;
use grid_util::grid::ValueGrid;
use smallvec::{smallvec, SmallVec};
use std::hint::black_box;

fn dao_bench(c: &mut Criterion) {
for (allow_diag, pruning) in [(true, false), (true, true)] {
let bench_set = if allow_diag {
["dao/arena", "dao/den312d", "dao/arena2"]
} else {
["dao/arena", "dao/den009d", "dao/den312d"]
};
fn dao_bench<const ALLOW_DIAGONAL: bool>(c: &mut Criterion) {
let arr: SmallVec<[bool; 2]> = if ALLOW_DIAGONAL {
smallvec![false]
} else {
smallvec![false]
};
let bench_set = if ALLOW_DIAGONAL {
["dao/arena2"]
} else {
["dao/arena2"]
};
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);
let mut pathing_grid: Pathfinder<ALLOW_DIAGONAL> =
Pathfinder::new(bool_grid.width, bool_grid.height, true);
pathing_grid.grid = bool_grid.clone();
pathing_grid.allow_diagonal_move = allow_diag;
pathing_grid.improved_pruning = pruning;
pathing_grid.initialize();
pathing_grid.generate_components();
let diag_str = if allow_diag { "8-grid" } else { "4-grid" };
let diag_str = if ALLOW_DIAGONAL { "8-grid" } else { "4-grid" };
let improved_str = if pruning { " (improved pruning)" } else { "" };

c.bench_function(format!("{name}, {diag_str}{improved_str}").as_str(), |b| {
b.iter(|| {
for (start, end) in &scenarios {
black_box(pathing_grid.get_path_single_goal(*start, *end, false));
for (start, end, _) in &scenarios {
black_box(pathing_grid.get_path_single_goal(*start, *end));
}
})
});
}
}
}

criterion_group!(benches, dao_bench);
fn dao_bench_solver<const ALLOW_DIAGONAL: bool, S, FS>(
c: &mut Criterion,
solver_name: &str,
create_solver: FS,
) where
S: GridSolver,
FS: Fn(&mut PathingGrid<ALLOW_DIAGONAL>) -> S,
{
let bench_set = if ALLOW_DIAGONAL {
["dao/arena2"]
} else {
["dao/arena2"]
};
for name in bench_set {
let (bool_grid, scenarios) = get_benchmark(name.to_owned());
let mut pathing_grid: PathingGrid<ALLOW_DIAGONAL> =
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}, {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));
}
})
});
}
}

fn dao_bench_jps<const ALLOW_DIAGONAL: bool>(c: &mut Criterion) {
let arr: SmallVec<[bool; 2]> = if ALLOW_DIAGONAL {
smallvec![false]
} else {
smallvec![false]
};
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<ALLOW_DIAGONAL>| {
let mut solver = JPSSolver::new(&pathing_grid, pruning);
solver.initialize(&pathing_grid);
solver
},
);
}
}
fn dao_bench_astar<const ALLOW_DIAGONAL: bool>(c: &mut Criterion) {
dao_bench_solver(c, "Astar", |_: &mut PathingGrid<ALLOW_DIAGONAL>| {
AstarSolver::new()
});
}

fn dao_bench_dijkstra<const ALLOW_DIAGONAL: bool>(c: &mut Criterion) {
dao_bench_solver(c, "Dijkstra", |_: &mut PathingGrid<ALLOW_DIAGONAL>| {
DijkstraSolver
});
}

criterion_group!(benches, dao_bench<true>, dao_bench_jps<true>,);
criterion_main!(benches);
24 changes: 13 additions & 11 deletions benches/single_bench.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
use criterion::{criterion_group, criterion_main, Criterion};
use grid_pathfinding::PathingGrid;
use grid_pathfinding::{
pathing_grid::PathingGrid,
solver::{jps::JPSSolver, GridSolver},
};
use grid_pathfinding_benchmark::*;
use grid_util::grid::ValueGrid;
use std::hint::black_box;

fn dao_bench_single(c: &mut Criterion) {
for (allow_diag, pruning) in [(true, false)] {
fn dao_bench_single<const ALLOW_DIAGONAL: bool>(c: &mut Criterion) {
for pruning in [false] {
let bench_set = ["dao/arena"];
for name in bench_set {
let (bool_grid, scenarios) = get_benchmark(name.to_owned());
let mut pathing_grid: PathingGrid =
let mut pathing_grid: PathingGrid<ALLOW_DIAGONAL> =
PathingGrid::new(bool_grid.width, bool_grid.height, true);
pathing_grid.grid = bool_grid.clone();
pathing_grid.allow_diagonal_move = allow_diag;
pathing_grid.improved_pruning = pruning;
pathing_grid.initialize();
pathing_grid.generate_components();
let diag_str = if allow_diag { "8-grid" } else { "4-grid" };
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 { "" };

c.bench_function(format!("{name}, {diag_str}{improved_str}").as_str(), |b| {
b.iter(|| {
for (start, end) in &scenarios {
black_box(pathing_grid.get_path_single_goal(*start, *end, false));
for (start, end, _) in &scenarios {
black_box(solver.get_path_single_goal(&mut pathing_grid, *start, *end));
}
})
});
}
}
}

criterion_group!(benches, dao_bench_single);
criterion_group!(benches, dao_bench_single<false>, dao_bench_single<true>);
criterion_main!(benches);
46 changes: 23 additions & 23 deletions examples/benchmark_runner.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use grid_pathfinding::PathingGrid;
use grid_pathfinding::Pathfinder;
use grid_pathfinding_benchmark::*;
use grid_util::grid::ValueGrid;
use grid_util::point::Point;
Expand All @@ -12,32 +12,32 @@ fn main() {

let (bool_grid, scenarios) = get_benchmark(name);
// for (allow_diag, pruning) in [(false, false), (true, false), (true, true)] {
for (allow_diag, pruning) in [(true, false)] {
let mut pathing_grid: PathingGrid =
PathingGrid::new(bool_grid.width, bool_grid.height, true);
pathing_grid.grid = bool_grid.clone();
pathing_grid.allow_diagonal_move = allow_diag;
pathing_grid.improved_pruning = pruning;
pathing_grid.initialize();
pathing_grid.generate_components();
let number_of_scenarios = scenarios.len() as u32;
let before = Instant::now();
run_scenarios(&pathing_grid, &scenarios);
let elapsed = before.elapsed();
println!(
"\tElapsed time: {:.2?}; per scenario: {:.2?}",
elapsed,
elapsed / number_of_scenarios
);
total_time += elapsed;
}
let mut pathing_grid: Pathfinder<true> =
Pathfinder::new(bool_grid.width, bool_grid.height, true);
pathing_grid.grid = bool_grid.clone();
pathing_grid.improved_pruning = false;
pathing_grid.initialize();
pathing_grid.generate_components();
let number_of_scenarios = scenarios.len() as u32;
let before = Instant::now();
run_scenarios(&pathing_grid, &scenarios);
let elapsed = before.elapsed();
println!(
"\tElapsed time: {:.2?}; per scenario: {:.2?}",
elapsed,
elapsed / number_of_scenarios
);
total_time += elapsed;
}
println!("\tTotal benchmark time: {:.2?}", total_time);
}

pub fn run_scenarios(pathing_grid: &PathingGrid, scenarios: &Vec<(Point, Point)>) {
for (start, goal) in scenarios {
let path: Option<Vec<Point>> = pathing_grid.get_waypoints_single_goal(*start, *goal, false);
pub fn run_scenarios<const ALLOW_DIAGONAL: bool>(
pathing_grid: &Pathfinder<ALLOW_DIAGONAL>,
scenarios: &Vec<(Point, Point, f64)>,
) {
for (start, goal, _) in scenarios {
let path: Option<Vec<Point>> = pathing_grid.get_waypoints_single_goal(*start, *goal);
assert!(path.is_some());
}
}
8 changes: 3 additions & 5 deletions examples/heuristic_factor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use grid_pathfinding::PathingGrid;
use grid_pathfinding::Pathfinder;
use grid_util::grid::ValueGrid;
use grid_util::point::Point;
use grid_util::Rect;
Expand All @@ -8,7 +8,7 @@ use grid_util::Rect;

fn main() {
const N: i32 = 30;
let mut pathing_grid: PathingGrid = PathingGrid::new(N as usize, N as usize, true);
let mut pathing_grid: Pathfinder<true> = Pathfinder::new(N as usize, N as usize, true);
pathing_grid.heuristic_factor = 1.3;
pathing_grid.set_rect(Rect::new(1, 1, N - 2, N - 2), false);
pathing_grid.set_rect(Rect::new(8, 8, 8, 8), true);
Expand All @@ -18,9 +18,7 @@ fn main() {
println!("{}", pathing_grid);
let start = Point::new(1, 1);
let end = Point::new(N - 3, N - 3);
let path = pathing_grid
.get_path_single_goal(start, end, false)
.unwrap();
let path = pathing_grid.get_path_single_goal(start, end).unwrap();
println!("Path:");
for p in path {
println!("{:?}", p);
Expand Down
4 changes: 2 additions & 2 deletions examples/multiple_goals.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use grid_pathfinding::PathingGrid;
use grid_pathfinding::Pathfinder;
use grid_util::grid::ValueGrid;
use grid_util::point::Point;

Expand All @@ -15,7 +15,7 @@ use grid_util::point::Point;
// The found path moves to the closest goal, which is the top one.

fn main() {
let mut pathing_grid: PathingGrid = PathingGrid::new(3, 3, false);
let mut pathing_grid: Pathfinder<true> = Pathfinder::new(3, 3, false);
pathing_grid.set(1, 1, true);
pathing_grid.generate_components();
println!("{}", pathing_grid);
Expand Down
10 changes: 4 additions & 6 deletions examples/paths_and_waypoints.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use grid_pathfinding::{waypoints_to_path, PathingGrid};
use grid_pathfinding::{waypoints_to_path, Pathfinder};
use grid_util::grid::ValueGrid;
use grid_util::point::Point;

Expand All @@ -20,13 +20,13 @@ use grid_util::point::Point;
// path, as a shorthand for the two previous calls.

fn main() {
let mut pathing_grid: PathingGrid = PathingGrid::new(5, 5, false);
let mut pathing_grid: Pathfinder<true> = Pathfinder::new(5, 5, false);
pathing_grid.set(1, 1, true);
pathing_grid.generate_components();
println!("{}", pathing_grid);
let start = Point::new(0, 0);
let end = Point::new(4, 4);
if let Some(path) = pathing_grid.get_waypoints_single_goal(start, end, false) {
if let Some(path) = pathing_grid.get_waypoints_single_goal(start, end) {
println!("Waypoints:");
for p in &path {
println!("{:?}", p);
Expand All @@ -37,9 +37,7 @@ fn main() {
}
}
println!("\nDirectly computed path");
let expanded_path = pathing_grid
.get_path_single_goal(start, end, false)
.unwrap();
let expanded_path = pathing_grid.get_path_single_goal(start, end).unwrap();
for p in expanded_path {
println!("{:?}", p);
}
Expand Down
9 changes: 3 additions & 6 deletions examples/simple_4.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use grid_pathfinding::PathingGrid;
use grid_pathfinding::Pathfinder;
use grid_util::grid::ValueGrid;
use grid_util::point::Point;

Expand All @@ -16,16 +16,13 @@ use grid_util::point::Point;
// Nodes have a 4-neighborhood

fn main() {
let mut pathing_grid: PathingGrid = PathingGrid::new(3, 3, false);
pathing_grid.allow_diagonal_move = false;
let mut pathing_grid: Pathfinder<false> = Pathfinder::new(3, 3, false);
pathing_grid.set(1, 1, true);
pathing_grid.generate_components();
println!("{}", pathing_grid);
let start = Point::new(0, 0);
let end = Point::new(2, 2);
let path = pathing_grid
.get_path_single_goal(start, end, false)
.unwrap();
let path = pathing_grid.get_path_single_goal(start, end).unwrap();
println!("Path:");
for p in path {
println!("{:?}", p);
Expand Down
8 changes: 3 additions & 5 deletions examples/simple_8.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use grid_pathfinding::PathingGrid;
use grid_pathfinding::Pathfinder;
use grid_util::grid::ValueGrid;
use grid_util::point::Point;

Expand All @@ -16,15 +16,13 @@ use grid_util::point::Point;
// Nodes have an 8-neighborhood

fn main() {
let mut pathing_grid: PathingGrid = PathingGrid::new(3, 3, false);
let mut pathing_grid: Pathfinder<true> = Pathfinder::new(3, 3, false);
pathing_grid.set(1, 1, true);
pathing_grid.generate_components();
println!("{}", pathing_grid);
let start = Point::new(0, 0);
let end = Point::new(2, 2);
let path = pathing_grid
.get_path_single_goal(start, end, false)
.unwrap();
let path = pathing_grid.get_path_single_goal(start, end).unwrap();
println!("Path:");
for p in path {
println!("{:?}", p);
Expand Down
Loading