extern crate basic_pathfinding; use basic_pathfinding::coord::Coord; use basic_pathfinding::grid::{Grid, GridType}; use basic_pathfinding::pathfinding::{find_path, SearchOpts}; macro_rules! hashmap { ($( $key: expr => $val: expr ),*) => {{ let mut map = ::std::collections::HashMap::new(); $( map.insert($key, $val); )* map }} } #[test] fn 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![1, 1, 1, 1, 1], vec![1, 1, 1, 1, 1], ], walkable_tiles: vec![1], ..Grid::default() }; let start = Coord::new(1, 2); let end = Coord::new(3, 2); let opts = SearchOpts::default(); let path = find_path(&grid, start, end, opts); assert_eq!( path.unwrap(), vec![ Coord::new(1, 3), Coord::new(2, 3), Coord::new(3, 3), Coord::new(3, 2), ] ); } #[test] fn path_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, 1, 1, 1], vec![1, 1, 1, 1, 1], ], walkable_tiles: vec![1], unwalkable_coords: hashmap![3 => hashmap![ 2 => true, 3 => true ]], ..Grid::default() }; let start = Coord::new(1, 2); let end = Coord::new(3, 2); let opts = SearchOpts::default(); let path = find_path(&grid, start, end, opts); assert_eq!( path.unwrap(), vec![ Coord::new(1, 3), Coord::new(1, 4), Coord::new(2, 4), Coord::new(3, 4), Coord::new(4, 4), Coord::new(4, 3), Coord::new(4, 2), Coord::new(3, 2) ] ); } #[test] fn early_returns() { 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 start = Coord::new(1, 2); let end = Coord::new(1, 2); let opts = SearchOpts::default(); let path = find_path(&grid, start, end, opts); assert_eq!(path.unwrap(), vec![]); } #[test] fn none_when_cannot_find_path() { 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], ..Grid::default() }; let start = Coord::new(0, 2); let end = Coord::new(4, 2); let opts = SearchOpts::default(); let path = find_path(&grid, start, end, opts); assert!(path.is_none()); } #[test] fn none_when_not_walkable() { 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], unwalkable_coords: hashmap![2 => hashmap![4 => true]], ..Grid::default() }; let start = Coord::new(0, 2); let end = Coord::new(4, 2); let opts = SearchOpts::default(); let path = find_path(&grid, start, end, opts); assert!(path.is_none()); } #[test] fn none_when_target_unstoppable() { 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], unstoppable_coords: hashmap![ 2 => hashmap![4 => true] ], ..Grid::default() }; let start = Coord::new(0, 2); let end = Coord::new(4, 2); let opts = SearchOpts::default(); let path = find_path(&grid, start, end, opts); assert!(path.is_none()); } #[test] fn accepts_opt_to_end_on_unstoppable() { 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], unstoppable_coords: hashmap![2 => hashmap![2 => true]], ..Grid::default() }; let start = Coord::new(0, 2); let end = Coord::new(4, 2); let opts = SearchOpts { end_on_unstoppable: true, ..SearchOpts::default() }; let path = find_path(&grid, start, end, opts); assert_eq!( path.unwrap(), vec![ Coord::new(1, 2), Coord::new(2, 2), Coord::new(3, 2), Coord::new(4, 2) ] ); } #[test] fn prefers_straight_paths() { let grid = Grid { tiles: vec![ vec![0, 0, 0, 0, 0], vec![0, 0, 0, 0, 0], vec![0, 0, 0, 0, 0], vec![0, 0, 0, 0, 0], vec![0, 0, 0, 0, 0], ], walkable_tiles: vec![0], ..Grid::default() }; let start = Coord::new(0, 2); let end = Coord::new(4, 2); let opts = SearchOpts::default(); let path = find_path(&grid, start, end, opts); assert_eq!( path.unwrap(), vec![ Coord::new(1, 2), Coord::new(2, 2), Coord::new(3, 2), Coord::new(4, 2), ] ); } #[test] fn respects_costs() { let grid = Grid { tiles: vec![ vec![0, 2, 2, 2, 0], vec![0, 2, 2, 2, 0], vec![0, 2, 2, 2, 0], vec![0, 1, 1, 1, 0], vec![0, 1, 1, 1, 0], ], walkable_tiles: vec![0, 1, 2], costs: hashmap![2 => 4], ..Grid::default() }; let start = Coord::new(0, 2); let end = Coord::new(4, 2); let opts = SearchOpts::default(); let path = find_path(&grid, start, end, opts); assert_eq!( path.unwrap(), [ Coord::new(0, 3), Coord::new(1, 3), Coord::new(2, 3), Coord::new(3, 3), Coord::new(4, 3), Coord::new(4, 2), ] ); } #[test] fn respects_extra_costs() { let grid = Grid { tiles: vec![ vec![0, 2, 2, 2, 0], vec![0, 2, 2, 2, 0], vec![0, 2, 2, 2, 0], vec![0, 1, 1, 1, 0], vec![0, 1, 1, 1, 0], ], walkable_tiles: vec![0, 1], extra_costs: hashmap![ 3 => hashmap![1 => 4], 4 => hashmap![3 => 4] ], ..Grid::default() }; let start = Coord::new(0, 2); let end = Coord::new(4, 2); let opts = SearchOpts::default(); let path = find_path(&grid, start, end, opts); assert_eq!( path.unwrap(), vec![ Coord::new(0, 3), Coord::new(0, 4), Coord::new(1, 4), Coord::new(2, 4), Coord::new(2, 3), Coord::new(3, 3), Coord::new(4, 3), Coord::new(4, 2), ] ); } #[test] fn path_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 start = Coord::new(1, 2); let end = Coord::new(3, 2); let mut opts = SearchOpts { cost_threshold: Some(3), ..SearchOpts::default() }; let path = find_path(&grid, start, end, opts); assert!(path.is_none()); opts = SearchOpts { cost_threshold: Some(4), ..SearchOpts::default() }; let path = find_path(&grid, start, end, opts); assert_eq!( path.unwrap(), vec![ Coord::new(1, 3), Coord::new(2, 3), Coord::new(3, 3), Coord::new(3, 2), ] ); } #[test] fn path_terminates_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 start = Coord::new(1, 2); let end = Coord::new(3, 2); let opts = SearchOpts { cost_threshold: Some(2), path_to_threshold: true, ..SearchOpts::default() }; let path = find_path(&grid, start, end, opts); assert_eq!(path.unwrap(), vec![Coord::new(1, 3), Coord::new(2, 3)]); } #[test] fn path_navigates_to_adjacent_coord() { 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 start = Coord::new(1, 2); let end = Coord::new(3, 2); let opts = SearchOpts { path_adjacent: true, ..SearchOpts::default() }; let path = find_path(&grid, start, end, opts); assert_eq!( path.unwrap(), vec![Coord::new(1, 3), Coord::new(2, 3), Coord::new(3, 3)] ); } #[test] fn path_navigates_to_adjacent_walkable_coord() { 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], unstoppable_coords: hashmap![ 2 => hashmap![4 => true], 3 => hashmap![3 => true] ], ..Grid::default() }; let start = Coord::new(1, 2); let end = Coord::new(3, 2); let opts = SearchOpts { path_adjacent: true, ..SearchOpts::default() }; let path = find_path(&grid, start, end, opts); assert_eq!( path.unwrap(), vec![ Coord::new(1, 3), Coord::new(2, 3), Coord::new(3, 3), Coord::new(3, 2), Coord::new(3, 1) ] ); } #[test] fn path_navigates_hex_grids() { let grid = Grid { tiles: vec![ vec![1, 1, 0, 1, 1], vec![1, 1, 0, 1, 1], vec![1, 0, 1, 0, 1], vec![1, 1, 0, 1, 1], vec![1, 1, 1, 1, 1], ], walkable_tiles: vec![1], grid_type: GridType::Hex, ..Grid::default() }; let start = Coord::new(1, 1); let end = Coord::new(2, 2); let opts = SearchOpts::default(); let path = find_path(&grid, start, end, opts); assert_eq!( path.unwrap(), vec![ Coord::new(0, 2), Coord::new(0, 3), Coord::new(1, 3), Coord::new(2, 2), ] ); } #[test] fn path_navigates_intercardinal_grids() { let grid = Grid { tiles: vec![ vec![1, 1, 0, 1, 1], vec![1, 1, 0, 1, 1], vec![1, 0, 1, 0, 1], vec![1, 1, 0, 1, 1], vec![1, 1, 1, 1, 1], ], walkable_tiles: vec![1], grid_type: GridType::Intercardinal, ..Grid::default() }; let start = Coord::new(1, 1); let end = Coord::new(3, 3); let opts = SearchOpts::default(); let path = find_path(&grid, start, end, opts); assert_eq!(path.unwrap(), vec![Coord::new(2, 2), Coord::new(3, 3),]); }