basic-pathfinding 0.2.8

Tile-based A* pathfinding in Rust
Documentation
extern crate basic_pathfinding;
use basic_pathfinding::coord::Coord;
use basic_pathfinding::grid::{Grid, GridType};
use basic_pathfinding::pathfinding::{find_reachable, SearchOpts};

macro_rules! hashmap {
  ($( $key: expr => $val: expr ),*) => {{
    let mut map = ::std::collections::HashMap::new();
    $( map.insert($key, $val); )*
    map
  }}
}

#[test]
fn only_traverses_walkable_tiles() {
  let grid = Grid {
    tiles: vec![
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
      vec![2, 2, 1, 1, 1],
      vec![1, 1, 1, 1, 1],
    ],
    walkable_tiles: vec![1],
    ..Grid::default()
  };
  let source = vec![Coord::new(1, 2)];
  let opts = SearchOpts::default();
  let result = find_reachable(&grid, source, opts);

  assert_eq!(
    result.stoppable,
    vec![
      Coord::new(0, 0),
      Coord::new(0, 1),
      Coord::new(0, 2),
      Coord::new(1, 0),
      Coord::new(1, 1),
      Coord::new(1, 2),
    ]
  );
}

#[test]
fn searches_from_multiple_sources() {
  let grid = Grid {
    tiles: vec![
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
      vec![2, 2, 2, 2, 2],
      vec![1, 1, 1, 1, 1],
    ],
    walkable_tiles: vec![1],
    ..Grid::default()
  };
  let source = vec![Coord::new(1, 2), Coord::new(4, 2)];
  let opts = SearchOpts::default();
  let result = find_reachable(&grid, source, opts);

  println!("asdf {:?}", result);

  assert_eq!(
    result.stoppable,
    vec![
      Coord { x: 0, y: 0 },
      Coord { x: 0, y: 1 },
      Coord { x: 0, y: 2 },
      Coord { x: 1, y: 0 },
      Coord { x: 1, y: 1 },
      Coord { x: 1, y: 2 },
      Coord { x: 3, y: 0 },
      Coord { x: 3, y: 1 },
      Coord { x: 3, y: 2 },
      Coord { x: 4, y: 0 },
      Coord { x: 4, y: 1 },
      Coord { x: 4, y: 2 }
    ]
  );
}

#[test]
fn avoids_unwalkable_coords() {
  let grid = Grid {
    tiles: vec![
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
    ],
    walkable_tiles: vec![1],
    unwalkable_coords: hashmap![
      3 => hashmap![0 => true, 1 => true]
    ],
    ..Grid::default()
  };

  let source = vec![Coord::new(1, 2)];
  let opts = SearchOpts::default();
  let result = find_reachable(&grid, source, opts);

  assert_eq!(
    result.stoppable,
    vec![
      Coord::new(0, 0),
      Coord::new(0, 1),
      Coord::new(0, 2),
      Coord::new(1, 0),
      Coord::new(1, 1),
      Coord::new(1, 2),
    ]
  );
}

#[test]
fn avoids_unstoppable_coords() {
  let grid = Grid {
    tiles: vec![
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
    ],
    walkable_tiles: vec![1],
    unstoppable_coords: hashmap![3 => hashmap![
      0 => true, 1 => true
    ]],
    ..Grid::default()
  };

  let source = vec![Coord::new(1, 2)];
  let opts = SearchOpts::default();
  let result = find_reachable(&grid, source, opts);

  assert_eq!(result.walkable, vec![Coord::new(0, 3), Coord::new(1, 3)]);
  assert_eq!(
    result.stoppable,
    vec![
      Coord::new(0, 0),
      Coord::new(0, 1),
      Coord::new(0, 2),
      Coord::new(0, 4),
      Coord::new(1, 0),
      Coord::new(1, 1),
      Coord::new(1, 2),
      Coord::new(1, 4),
    ]
  );
}

#[test]
fn cancels_early_with_cost_threshold() {
  let grid = Grid {
    tiles: vec![
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 0, 1, 1],
      vec![1, 1, 1, 1, 1],
      vec![1, 1, 1, 1, 1],
    ],
    walkable_tiles: vec![1],
    ..Grid::default()
  };

  let source = vec![Coord::new(1, 2)];
  let opts = SearchOpts {
    cost_threshold: Some(1),
    ..SearchOpts::default()
  };
  let result = find_reachable(&grid, source, opts);

  assert_eq!(
    result.stoppable,
    vec![
      Coord::new(0, 2),
      Coord::new(1, 1),
      Coord::new(1, 2),
      Coord::new(1, 3),
    ]
  );

  let source = vec![Coord::new(1, 2)];
  let opts = SearchOpts {
    cost_threshold: Some(4),
    ..SearchOpts::default()
  };
  let result = find_reachable(&grid, source, opts);

  assert_eq!(
    result.stoppable,
    vec![
      Coord::new(0, 0),
      Coord::new(0, 1),
      Coord::new(0, 2),
      Coord::new(0, 3),
      Coord::new(0, 4),
      Coord::new(1, 0),
      Coord::new(1, 1),
      Coord::new(1, 2),
      Coord::new(1, 3),
      Coord::new(1, 4),
      Coord::new(2, 3),
      Coord::new(2, 4),
      Coord::new(3, 2),
      Coord::new(3, 3),
      Coord::new(3, 4),
      Coord::new(4, 3),
    ]
  );
}

#[test]
fn reports_start_only_when_cost_zero() {
  let grid = Grid {
    tiles: vec![
      vec![1, 1, 1, 1, 1],
      vec![1, 1, 1, 1, 1],
      vec![1, 1, 1, 1, 1],
      vec![1, 1, 1, 1, 1],
      vec![1, 1, 1, 1, 1],
    ],
    walkable_tiles: vec![1],
    ..Grid::default()
  };

  let source = vec![Coord::new(1, 2)];
  let opts = SearchOpts {
    cost_threshold: Some(0),
    ..SearchOpts::default()
  };
  let result = find_reachable(&grid, source, opts);

  assert_eq!(result.stoppable, vec![Coord::new(1, 2),]);
}

#[test]
fn doesnt_include_unwalkable_start() {
  let grid = Grid {
    tiles: vec![
      vec![1, 1, 1, 1, 1],
      vec![1, 1, 1, 1, 1],
      vec![1, 1, 1, 1, 1],
      vec![1, 1, 1, 1, 1],
      vec![1, 1, 1, 1, 1],
    ],
    walkable_tiles: vec![],
    ..Grid::default()
  };

  let source = vec![Coord::new(1, 2)];
  let opts = SearchOpts {
    cost_threshold: Some(4),
    ..SearchOpts::default()
  };
  let result = find_reachable(&grid, source, opts);

  assert_eq!(result.stoppable, vec![]);
}

#[test]
fn navigates_hex_grids() {
  let grid = Grid {
    tiles: vec![
      vec![1, 0, 1, 0, 1],
      vec![0, 1, 0, 0, 1],
      vec![1, 0, 1, 0, 1],
      vec![0, 1, 0, 0, 1],
      vec![1, 1, 0, 1, 1],
    ],
    walkable_tiles: vec![1],
    grid_type: GridType::Hex,
    ..Grid::default()
  };

  let source = vec![Coord::new(1, 1)];
  let opts = SearchOpts::default();
  let result = find_reachable(&grid, source, opts);

  assert_eq!(
    result.stoppable,
    vec![Coord::new(0, 2), Coord::new(1, 1), Coord::new(2, 0),]
  );
}

#[test]
fn navigates_intercardinal_grids() {
  let grid = Grid {
    tiles: vec![
      vec![1, 0, 0, 0, 0],
      vec![0, 1, 0, 0, 0],
      vec![0, 1, 0, 0, 0],
      vec![1, 0, 0, 0, 0],
      vec![0, 1, 0, 0, 0],
    ],
    walkable_tiles: vec![1],
    grid_type: GridType::Intercardinal,
    ..Grid::default()
  };

  let source = vec![Coord::new(1, 1)];
  let opts = SearchOpts::default();
  let result = find_reachable(&grid, source, opts);

  assert_eq!(
    result.stoppable,
    vec![
      Coord::new(0, 0),
      Coord::new(0, 3),
      Coord::new(1, 1),
      Coord::new(1, 2),
      Coord::new(1, 4),
    ]
  );
}