Skip to content

Commit

Permalink
Lookup precomputed aggressive path
Browse files Browse the repository at this point in the history
  • Loading branch information
JesseEmond committed Oct 7, 2024
1 parent 830ae44 commit ac7fddf
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 69 deletions.
16 changes: 11 additions & 5 deletions bot/benches/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use pprof::criterion::{Output, PProfProfiler};

use devnull_bot::grid::{make_grid, Move, Pos};
use devnull_bot::mcts;
use devnull_bot::pathfinding::{get_aggressive_path, PathfindingGrid};
use devnull_bot::pathfinding::{FastAggressivePathfinder, Pathfinder, PathfindingGrid};
use devnull_bot::search;
use devnull_bot::simulation;
use devnull_bot::simulation::Style;
Expand Down Expand Up @@ -36,14 +36,20 @@ pub fn criterion_benchmark(c: &mut Criterion) {
"######################",
]);
c.bench_function("get_aggressive_path 22x15 far", |b| {
b.iter(|| get_aggressive_path(
b.iter(|| {
let mut pathfinder = FastAggressivePathfinder::new(black_box(&grid));
pathfinder.pathfind(
black_box(&grid), black_box(&Pos { x: 5, y: 1 }),
black_box(&Pos { x: 18, y: 13 })));
black_box(&Some(Pos { x: 18, y: 13 })))
});
});
c.bench_function("get_aggressive_path 22x15 close", |b| {
b.iter(|| get_aggressive_path(
b.iter(|| {
let mut pathfinder = FastAggressivePathfinder::new(black_box(&grid));
pathfinder.pathfind(
black_box(&grid), black_box(&Pos { x: 5, y: 1 }),
black_box(&Pos { x: 9, y: 1 })));
black_box(&Some(Pos { x: 9, y: 1 })))
});
});
c.bench_function("PathfindingGrid 22x15 creation", |b| {
b.iter(|| PathfindingGrid::new(black_box(grid.clone())));
Expand Down
34 changes: 11 additions & 23 deletions bot/src/pathfinding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub type Path = Vec<Pos>;
type Node = EmptyTile;

/// State of pathfinding from a given start point.
struct PathfinderState {
pub struct PathfinderState {
/// For a given 'empty_tiles' index, its shortest distance to a start point.
cost: Vec<Cost>,
/// For a given 'empty_tiles' index, previous pos on shortest path.
Expand Down Expand Up @@ -58,7 +58,7 @@ impl PathfinderState {
}
}

trait Pathfinder {
pub trait Pathfinder {
/// Next node to explore in our search, if we should continue.
fn next_node(&mut self, state: &PathfinderState) -> Option<Node>;
/// Queue up a future note to visit.
Expand Down Expand Up @@ -137,7 +137,7 @@ impl Pathfinder for SlowAggressivePathfinder {
/// - sort is called at the start of the loop, so any newly added nodes in
/// the same loop will keep their initial relative order from 'empty_tiles'
/// instead of being in the order seen
struct FastAggressivePathfinder {
pub struct FastAggressivePathfinder {
frontier: VecDeque<Node>,
/// Nodes at the head of 'frontier' have this cost.
frontier_cost: Cost,
Expand All @@ -148,7 +148,7 @@ struct FastAggressivePathfinder {
}

impl FastAggressivePathfinder {
fn new(_grid: &Grid) -> Self {
pub fn new(_grid: &Grid) -> Self {
Self {
frontier: VecDeque::new(),
frontier_cost: 0,
Expand Down Expand Up @@ -212,8 +212,8 @@ impl PathfindingGrid {
pub fn new(grid: Grid) -> Self {
let mut pathfinding_states = Vec::new();
for pos in &grid.empty_tiles {
// TODO: Consider using a non "aggressive" pathfinder -- can be a
// regularly optimized one not matching the JS details.
// Note: deliberate using the 'aggressive.js' version of pathfinding
// for fast 'get_aggressive_path' lookups.
let mut pathfinder = FastAggressivePathfinder::new(&grid);
pathfinding_states.push(pathfinder.pathfind(&grid, &pos, &None));
}
Expand Down Expand Up @@ -247,12 +247,12 @@ impl PathfindingGrid {
}
path
}
}

pub fn get_aggressive_path(grid: &Grid, from: &Pos, to: &Pos) -> Vec<Pos> {
let mut pathfinder = FastAggressivePathfinder::new(grid);
let state = pathfinder.pathfind(grid, from, &Some(*to));
state.get_path(grid, to)
/// Get path the same way that 'aggressive.js' would find it.
pub fn get_aggressive_path(&self, from: &Pos, to: &Pos) -> Vec<Pos> {
let from_idx = self.grid.empty_tile_idx(from);
self.pathfinding_states[from_idx].get_path(&self.grid, to)
}
}

#[cfg(test)]
Expand Down Expand Up @@ -280,18 +280,6 @@ mod tests {
])
}

#[test]
fn test_slow_pathfinder_same_path_as_get_aggressive_path() {
let grid = make_test_grid();
let from = Pos { x: 5, y: 1 };
let to = Pos { x: 18, y: 13 };

let mut pathfinder = SlowAggressivePathfinder::new(&grid);
let path = pathfinder.pathfind(&grid, &from, &Some(to))
.get_path(&grid, &to);
assert_eq!(path, get_aggressive_path(&grid, &from, &to));
}

#[test]
fn test_slow_pathfinder_same_path_as_fast_pathfinder() {
let grid = make_test_grid();
Expand Down
43 changes: 2 additions & 41 deletions bot/src/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use strum_macros::EnumIter;
use once_cell::sync::Lazy;

use crate::grid::{Grid, Move, Pos};
use crate::pathfinding::{get_aggressive_path, PathfindingGrid};
use crate::pathfinding::{PathfindingGrid};

// TODO: No longer need to precompute this, don't need to be stateless
const MAX_TICKS: usize = 10000;
Expand Down Expand Up @@ -155,7 +155,7 @@ impl Threat {
},
Style::Shark => {
// See aggressive.js
let path = get_aggressive_path(&grid.grid, &self.pos, &player_prev);
let path = grid.get_aggressive_path(&self.pos, &player_prev);
follow_path(&self.pos, &path)
},
Style::Owl => {
Expand Down Expand Up @@ -367,48 +367,9 @@ impl State {

#[cfg(test)]
mod tests {
use strum::IntoEnumIterator;
use super::*;
use super::super::grid::{make_grid};

fn make_test_game() -> Game {
let threats = Style::iter()
.map(|s| Threat::new(Pos { x: 1, y: 4 }, s, Move::Right))
.collect();
Game {
grid: make_grid(vec![
"#####",
"# #",
"# #",
"#####",
"# #",
"# #",
"#####",
]),
alive: true,
pos: Pos { x: 1, y: 1 },
tick: 1,
threats,
}
}

#[test]
fn test_apply_matches_simulate() {
let mut simulated = State::new(make_test_game());
let mut applied = simulated.clone();
for _ in 0..1500 {
let moves = simulated.generate_moves();
let direction = *moves.iter().next().unwrap();
simulated.simulate_tick(SimulationAction::Move { direction });
applied.apply(direction);
// All predictable threats, should have moved to next turn.
assert_eq!(simulated.tick, applied.tick);
assert_eq!(simulated.game_over, applied.game_over);
assert_eq!(simulated.pos, applied.pos);
assert_eq!(simulated.threats, applied.threats);
}
}

#[test]
fn test_owl_prediction_misprediction_repro() {
let grid = PathfindingGrid::new(make_grid(vec![
Expand Down

0 comments on commit ac7fddf

Please sign in to comment.