use std::{cmp::Ordering, collections::HashMap};
use crate::{BOARD_HEIGHT, BOARD_WIDTH, Coord, Tile, board::Board};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BeastAction {
PlayerKilled,
Moved,
Stayed,
}
pub trait Beast {
fn new(position: Coord) -> Self;
fn advance(&mut self, board: &mut Board, player_position: Coord) -> BeastAction;
fn get_score() -> u16;
fn is_walkable_tile(tile: &Tile) -> bool {
matches!(tile, Tile::Empty | Tile::Player)
}
fn get_walkable_coords(board: &Board, position: &Coord, player_position: &Coord, check_tiles: bool) -> Vec<Coord> {
let mut result = Vec::with_capacity(8);
let left_top: Coord = Coord {
column: position.column.saturating_sub(1),
row: position.row.saturating_sub(1),
};
let middle_top: Coord = Coord {
column: position.column,
row: position.row.saturating_sub(1),
};
let right_top: Coord = Coord {
column: std::cmp::min(position.column + 1, BOARD_WIDTH - 1),
row: position.row.saturating_sub(1),
};
let left_middle: Coord = Coord {
column: position.column.saturating_sub(1),
row: position.row,
};
let right_middle: Coord = Coord {
column: std::cmp::min(position.column + 1, BOARD_WIDTH - 1),
row: position.row,
};
let left_bottom: Coord = Coord {
column: position.column.saturating_sub(1),
row: std::cmp::min(position.row + 1, BOARD_HEIGHT - 1),
};
let middle_bottom: Coord = Coord {
column: position.column,
row: std::cmp::min(position.row + 1, BOARD_HEIGHT - 1),
};
let right_bottom: Coord = Coord {
column: std::cmp::min(position.column + 1, BOARD_WIDTH - 1),
row: std::cmp::min(position.row + 1, BOARD_HEIGHT - 1),
};
match (player_position.column.cmp(&position.column), player_position.row.cmp(&position.row)) {
(Ordering::Equal, Ordering::Greater) => {
if Self::is_walkable_tile(&board[middle_bottom]) || !check_tiles {
result.push(middle_bottom);
}
if !result.contains(&left_bottom) && Self::is_walkable_tile(&board[left_bottom]) || !check_tiles {
result.push(left_bottom);
}
if !result.contains(&right_bottom) && Self::is_walkable_tile(&board[right_bottom]) || !check_tiles {
result.push(right_bottom);
}
if !result.contains(&left_middle) && Self::is_walkable_tile(&board[left_middle]) || !check_tiles {
result.push(left_middle);
}
if !result.contains(&right_middle) && Self::is_walkable_tile(&board[right_middle]) || !check_tiles {
result.push(right_middle);
}
if !result.contains(&left_top) && Self::is_walkable_tile(&board[left_top]) || !check_tiles {
result.push(left_top);
}
if !result.contains(&right_top) && Self::is_walkable_tile(&board[right_top]) || !check_tiles {
result.push(right_top);
}
if !result.contains(&middle_top) && Self::is_walkable_tile(&board[middle_top]) || !check_tiles {
result.push(middle_top);
}
},
(Ordering::Equal, Ordering::Less) => {
if Self::is_walkable_tile(&board[middle_top]) || !check_tiles {
result.push(middle_top);
}
if !result.contains(&left_top) && Self::is_walkable_tile(&board[left_top]) || !check_tiles {
result.push(left_top);
}
if !result.contains(&right_top) && Self::is_walkable_tile(&board[right_top]) || !check_tiles {
result.push(right_top);
}
if !result.contains(&left_middle) && Self::is_walkable_tile(&board[left_middle]) || !check_tiles {
result.push(left_middle);
}
if !result.contains(&right_middle) && Self::is_walkable_tile(&board[right_middle]) || !check_tiles {
result.push(right_middle);
}
if !result.contains(&left_bottom) && Self::is_walkable_tile(&board[left_bottom]) || !check_tiles {
result.push(left_bottom);
}
if !result.contains(&right_bottom) && Self::is_walkable_tile(&board[right_bottom]) || !check_tiles {
result.push(right_bottom);
}
if !result.contains(&middle_bottom) && Self::is_walkable_tile(&board[middle_bottom]) || !check_tiles {
result.push(middle_bottom);
}
},
(Ordering::Less, Ordering::Equal) => {
if Self::is_walkable_tile(&board[left_middle]) || !check_tiles {
result.push(left_middle);
}
if !result.contains(&left_top) && Self::is_walkable_tile(&board[left_top]) || !check_tiles {
result.push(left_top);
}
if !result.contains(&left_bottom) && Self::is_walkable_tile(&board[left_bottom]) || !check_tiles {
result.push(left_bottom);
}
if !result.contains(&middle_top) && Self::is_walkable_tile(&board[middle_top]) || !check_tiles {
result.push(middle_top);
}
if !result.contains(&middle_bottom) && Self::is_walkable_tile(&board[middle_bottom]) || !check_tiles {
result.push(middle_bottom);
}
if !result.contains(&right_top) && Self::is_walkable_tile(&board[right_top]) || !check_tiles {
result.push(right_top);
}
if !result.contains(&right_bottom) && Self::is_walkable_tile(&board[right_bottom]) || !check_tiles {
result.push(right_bottom);
}
if !result.contains(&right_middle) && Self::is_walkable_tile(&board[right_middle]) || !check_tiles {
result.push(right_middle);
}
},
(Ordering::Greater, Ordering::Equal) => {
if Self::is_walkable_tile(&board[right_middle]) || !check_tiles {
result.push(right_middle);
}
if !result.contains(&right_top) && Self::is_walkable_tile(&board[right_top]) || !check_tiles {
result.push(right_top);
}
if !result.contains(&right_bottom) && Self::is_walkable_tile(&board[right_bottom]) || !check_tiles {
result.push(right_bottom);
}
if !result.contains(&middle_top) && Self::is_walkable_tile(&board[middle_top]) || !check_tiles {
result.push(middle_top);
}
if !result.contains(&middle_bottom) && Self::is_walkable_tile(&board[middle_bottom]) || !check_tiles {
result.push(middle_bottom);
}
if !result.contains(&left_top) && Self::is_walkable_tile(&board[left_top]) || !check_tiles {
result.push(left_top);
}
if !result.contains(&left_bottom) && Self::is_walkable_tile(&board[left_bottom]) || !check_tiles {
result.push(left_bottom);
}
if !result.contains(&left_middle) && Self::is_walkable_tile(&board[left_middle]) || !check_tiles {
result.push(left_middle);
}
},
(Ordering::Greater, Ordering::Greater) => {
if Self::is_walkable_tile(&board[right_bottom]) || !check_tiles {
result.push(right_bottom);
}
if !result.contains(&middle_bottom) && Self::is_walkable_tile(&board[middle_bottom]) || !check_tiles {
result.push(middle_bottom);
}
if !result.contains(&right_middle) && Self::is_walkable_tile(&board[right_middle]) || !check_tiles {
result.push(right_middle);
}
if !result.contains(&left_bottom) && Self::is_walkable_tile(&board[left_bottom]) || !check_tiles {
result.push(left_bottom);
}
if !result.contains(&right_top) && Self::is_walkable_tile(&board[right_top]) || !check_tiles {
result.push(right_top);
}
if !result.contains(&left_middle) && Self::is_walkable_tile(&board[left_middle]) || !check_tiles {
result.push(left_middle);
}
if !result.contains(&middle_top) && Self::is_walkable_tile(&board[middle_top]) || !check_tiles {
result.push(middle_top);
}
if !result.contains(&left_top) && Self::is_walkable_tile(&board[left_top]) || !check_tiles {
result.push(left_top);
}
},
(Ordering::Greater, Ordering::Less) => {
if Self::is_walkable_tile(&board[right_top]) || !check_tiles {
result.push(right_top);
}
if !result.contains(&middle_top) && Self::is_walkable_tile(&board[middle_top]) || !check_tiles {
result.push(middle_top);
}
if !result.contains(&right_middle) && Self::is_walkable_tile(&board[right_middle]) || !check_tiles {
result.push(right_middle);
}
if !result.contains(&left_top) && Self::is_walkable_tile(&board[left_top]) || !check_tiles {
result.push(left_top);
}
if !result.contains(&right_bottom) && Self::is_walkable_tile(&board[right_bottom]) || !check_tiles {
result.push(right_bottom);
}
if !result.contains(&left_middle) && Self::is_walkable_tile(&board[left_middle]) || !check_tiles {
result.push(left_middle);
}
if !result.contains(&middle_bottom) && Self::is_walkable_tile(&board[middle_bottom]) || !check_tiles {
result.push(middle_bottom);
}
if !result.contains(&left_bottom) && Self::is_walkable_tile(&board[left_bottom]) || !check_tiles {
result.push(left_bottom);
}
},
(Ordering::Less, Ordering::Greater) => {
if Self::is_walkable_tile(&board[left_bottom]) || !check_tiles {
result.push(left_bottom);
}
if !result.contains(&left_middle) && Self::is_walkable_tile(&board[left_middle]) || !check_tiles {
result.push(left_middle);
}
if !result.contains(&middle_bottom) && Self::is_walkable_tile(&board[middle_bottom]) || !check_tiles {
result.push(middle_bottom);
}
if !result.contains(&left_top) && Self::is_walkable_tile(&board[left_top]) || !check_tiles {
result.push(left_top);
}
if !result.contains(&right_bottom) && Self::is_walkable_tile(&board[right_bottom]) || !check_tiles {
result.push(right_bottom);
}
if !result.contains(&right_middle) && Self::is_walkable_tile(&board[right_middle]) || !check_tiles {
result.push(right_middle);
}
if !result.contains(&middle_top) && Self::is_walkable_tile(&board[middle_top]) || !check_tiles {
result.push(middle_top);
}
if !result.contains(&right_top) && Self::is_walkable_tile(&board[right_top]) || !check_tiles {
result.push(right_top);
}
},
(Ordering::Less, Ordering::Less) => {
if Self::is_walkable_tile(&board[left_top]) || !check_tiles {
result.push(left_top);
}
if !result.contains(&left_middle) && Self::is_walkable_tile(&board[left_middle]) || !check_tiles {
result.push(left_middle);
}
if !result.contains(&middle_top) && Self::is_walkable_tile(&board[middle_top]) || !check_tiles {
result.push(middle_top);
}
if !result.contains(&left_bottom) && Self::is_walkable_tile(&board[left_bottom]) || !check_tiles {
result.push(left_bottom);
}
if !result.contains(&right_top) && Self::is_walkable_tile(&board[right_top]) || !check_tiles {
result.push(right_top);
}
if !result.contains(&middle_bottom) && Self::is_walkable_tile(&board[middle_bottom]) || !check_tiles {
result.push(middle_bottom);
}
if !result.contains(&right_middle) && Self::is_walkable_tile(&board[right_middle]) || !check_tiles {
result.push(right_middle);
}
if !result.contains(&right_bottom) && Self::is_walkable_tile(&board[right_bottom]) || !check_tiles {
result.push(right_bottom);
}
},
(Ordering::Equal, Ordering::Equal) => {
unreachable!("The beast can't be at the same place at the player")
},
};
result
}
fn heuristic(a: &Coord, b: &Coord) -> i32 {
let distance_column = (a.column as i32 - b.column as i32).abs();
let distance_row = (a.row as i32 - b.row as i32).abs();
distance_column.max(distance_row)
}
fn reconstruct_path(came_from: &HashMap<Coord, Coord>, mut current: Coord) -> Vec<Coord> {
let mut reconstructed_path = vec![current];
while let Some(&prev) = came_from.get(¤t) {
current = prev;
reconstructed_path.push(current);
}
reconstructed_path.reverse();
reconstructed_path
}
}
#[cfg(test)]
mod test {
use super::*;
use std::time::Instant;
struct DummyBeast;
impl Beast for DummyBeast {
fn new(_position: Coord) -> Self {
DummyBeast
}
fn advance(&mut self, _board: &mut Board, _player_position: Coord) -> BeastAction {
BeastAction::Moved
}
fn get_score() -> u16 {
0
}
}
#[test]
fn is_walkable_empty_tile_test() {
assert!(DummyBeast::is_walkable_tile(&Tile::Empty), "Tile::Empty should be walkable");
}
#[test]
fn is_walkable_player_tile_test() {
assert!(DummyBeast::is_walkable_tile(&Tile::Player), "Tile::Player should be walkable");
}
#[test]
fn is_walkable_blocked_tile_test() {
assert!(!DummyBeast::is_walkable_tile(&Tile::Block), "Tile::Block should not be walkable");
assert!(!DummyBeast::is_walkable_tile(&Tile::StaticBlock), "Tile::StaticBlock should not be walkable");
assert!(!DummyBeast::is_walkable_tile(&Tile::CommonBeast), "Tile::CommonBeast should not be walkable");
assert!(!DummyBeast::is_walkable_tile(&Tile::SuperBeast), "Tile::SuperBeast should not be walkable");
assert!(!DummyBeast::is_walkable_tile(&Tile::Egg(Instant::now())), "Tile::Egg should not be walkable");
assert!(
!DummyBeast::is_walkable_tile(&Tile::EggHatching(Instant::now())),
"Tile::EggHatching should not be walkable"
);
assert!(!DummyBeast::is_walkable_tile(&Tile::HatchedBeast), "Tile::HatchedBeast should not be walkable");
}
#[test]
fn get_walkable_coords_below_test() {
let mut board = Board::new([[Tile::Empty; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord { column: 5, row: 5 };
let player = Coord { column: 5, row: 7 };
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 5, row: 6 }, Coord { column: 4, row: 6 }, Coord { column: 6, row: 6 }, Coord { column: 4, row: 5 }, Coord { column: 6, row: 5 }, Coord { column: 4, row: 4 }, Coord { column: 6, row: 4 }, Coord { column: 5, row: 4 }, ],
"Player straight below should yield expected neighbor order"
);
board[Coord { column: 5, row: 6 }] = Tile::StaticBlock;
board[Coord { column: 4, row: 4 }] = Tile::Block;
board[Coord { column: 5, row: 4 }] = Tile::StaticBlock;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 4, row: 6 }, Coord { column: 6, row: 6 }, Coord { column: 4, row: 5 }, Coord { column: 6, row: 5 }, Coord { column: 6, row: 4 }, ],
"Player straight below should yield expected neighbor order with blocking tiles"
);
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, false),
vec![
Coord { column: 5, row: 6 }, Coord { column: 4, row: 6 }, Coord { column: 6, row: 6 }, Coord { column: 4, row: 5 }, Coord { column: 6, row: 5 }, Coord { column: 4, row: 4 }, Coord { column: 6, row: 4 }, Coord { column: 5, row: 4 }, ],
"Player straight below should yield expected neighbor order without checking for blocking tiles"
);
board[Coord { column: 4, row: 4 }] = Tile::Block;
board[Coord { column: 5, row: 4 }] = Tile::Block;
board[Coord { column: 6, row: 4 }] = Tile::Block;
board[Coord { column: 4, row: 5 }] = Tile::Block;
board[Coord { column: 6, row: 5 }] = Tile::Block;
board[Coord { column: 4, row: 6 }] = Tile::Block;
board[Coord { column: 5, row: 6 }] = Tile::Block;
board[Coord { column: 6, row: 6 }] = Tile::Block;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
Vec::new(),
"Player straight below should yield no neighbor for a blocked board"
);
}
#[test]
fn get_walkable_coords_above_test() {
let mut board = Board::new([[Tile::Empty; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord { column: 5, row: 5 };
let player = Coord { column: 5, row: 3 };
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 5, row: 4 }, Coord { column: 4, row: 4 }, Coord { column: 6, row: 4 }, Coord { column: 4, row: 5 }, Coord { column: 6, row: 5 }, Coord { column: 4, row: 6 }, Coord { column: 6, row: 6 }, Coord { column: 5, row: 6 }, ],
"Player straight above should yield expected neighbor order"
);
board[Coord { column: 5, row: 6 }] = Tile::Block;
board[Coord { column: 6, row: 6 }] = Tile::Block;
board[Coord { column: 6, row: 5 }] = Tile::Block;
board[Coord { column: 4, row: 5 }] = Tile::Block;
board[Coord { column: 4, row: 4 }] = Tile::Block;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 5, row: 4 }, Coord { column: 6, row: 4 }, Coord { column: 4, row: 6 }, ],
"Player straight above should yield expected neighbor order with blocking tiles"
);
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, false),
vec![
Coord { column: 5, row: 4 }, Coord { column: 4, row: 4 }, Coord { column: 6, row: 4 }, Coord { column: 4, row: 5 }, Coord { column: 6, row: 5 }, Coord { column: 4, row: 6 }, Coord { column: 6, row: 6 }, Coord { column: 5, row: 6 }, ],
"Player straight above should yield expected neighbor order without checking for blocking tiles"
);
board[Coord { column: 4, row: 4 }] = Tile::Block;
board[Coord { column: 5, row: 4 }] = Tile::Block;
board[Coord { column: 6, row: 4 }] = Tile::Block;
board[Coord { column: 4, row: 5 }] = Tile::Block;
board[Coord { column: 6, row: 5 }] = Tile::Block;
board[Coord { column: 4, row: 6 }] = Tile::Block;
board[Coord { column: 5, row: 6 }] = Tile::Block;
board[Coord { column: 6, row: 6 }] = Tile::Block;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
Vec::new(),
"Player straight above should yield no neighbor for a blocked board"
);
}
#[test]
fn get_walkable_coords_left_test() {
let mut board = Board::new([[Tile::Empty; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord { column: 5, row: 5 };
let player = Coord { column: 3, row: 5 };
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 4, row: 5 }, Coord { column: 4, row: 4 }, Coord { column: 4, row: 6 }, Coord { column: 5, row: 4 }, Coord { column: 5, row: 6 }, Coord { column: 6, row: 4 }, Coord { column: 6, row: 6 }, Coord { column: 6, row: 5 }, ],
"Player straight left should yield expected neighbor order"
);
board[Coord { column: 4, row: 5 }] = Tile::Block;
board[Coord { column: 4, row: 4 }] = Tile::Block;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 4, row: 6 }, Coord { column: 5, row: 4 }, Coord { column: 5, row: 6 }, Coord { column: 6, row: 4 }, Coord { column: 6, row: 6 }, Coord { column: 6, row: 5 }, ],
"Player straight left should yield expected neighbor order with blocking tiles"
);
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, false),
vec![
Coord { column: 4, row: 5 }, Coord { column: 4, row: 4 }, Coord { column: 4, row: 6 }, Coord { column: 5, row: 4 }, Coord { column: 5, row: 6 }, Coord { column: 6, row: 4 }, Coord { column: 6, row: 6 }, Coord { column: 6, row: 5 }, ],
"Player straight left should yield expected neighbor order without checking for blocking tiles"
);
board[Coord { column: 4, row: 4 }] = Tile::Block;
board[Coord { column: 5, row: 4 }] = Tile::Block;
board[Coord { column: 6, row: 4 }] = Tile::Block;
board[Coord { column: 4, row: 5 }] = Tile::Block;
board[Coord { column: 6, row: 5 }] = Tile::Block;
board[Coord { column: 4, row: 6 }] = Tile::Block;
board[Coord { column: 5, row: 6 }] = Tile::Block;
board[Coord { column: 6, row: 6 }] = Tile::Block;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
Vec::new(),
"Player straight left should yield no neighbor for a blocked board"
);
}
#[test]
fn get_walkable_coords_right_test() {
let mut board = Board::new([[Tile::Empty; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord { column: 5, row: 5 };
let player = Coord { column: 7, row: 5 };
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 6, row: 5 }, Coord { column: 6, row: 4 }, Coord { column: 6, row: 6 }, Coord { column: 5, row: 4 }, Coord { column: 5, row: 6 }, Coord { column: 4, row: 4 }, Coord { column: 4, row: 6 }, Coord { column: 4, row: 5 }, ],
"Player straight right should yield expected neighbor order"
);
board[Coord { column: 5, row: 4 }] = Tile::StaticBlock;
board[Coord { column: 5, row: 6 }] = Tile::StaticBlock;
board[Coord { column: 6, row: 6 }] = Tile::StaticBlock;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 6, row: 5 }, Coord { column: 6, row: 4 }, Coord { column: 4, row: 4 }, Coord { column: 4, row: 6 }, Coord { column: 4, row: 5 }, ],
"Player straight right should yield expected neighbor order with blocking tiles"
);
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, false),
vec![
Coord { column: 6, row: 5 }, Coord { column: 6, row: 4 }, Coord { column: 6, row: 6 }, Coord { column: 5, row: 4 }, Coord { column: 5, row: 6 }, Coord { column: 4, row: 4 }, Coord { column: 4, row: 6 }, Coord { column: 4, row: 5 }, ],
"Player straight right should yield expected neighbor order without checking for blocking tiles"
);
board[Coord { column: 4, row: 4 }] = Tile::Block;
board[Coord { column: 5, row: 4 }] = Tile::Block;
board[Coord { column: 6, row: 4 }] = Tile::Block;
board[Coord { column: 4, row: 5 }] = Tile::Block;
board[Coord { column: 6, row: 5 }] = Tile::Block;
board[Coord { column: 4, row: 6 }] = Tile::Block;
board[Coord { column: 5, row: 6 }] = Tile::Block;
board[Coord { column: 6, row: 6 }] = Tile::Block;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
Vec::new(),
"Player straight right should yield no neighbor for a blocked board"
);
}
#[test]
fn get_walkable_coords_below_right_test() {
let mut board = Board::new([[Tile::Empty; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord { column: 5, row: 5 };
let player = Coord { column: 7, row: 7 };
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 6, row: 6 }, Coord { column: 5, row: 6 }, Coord { column: 6, row: 5 }, Coord { column: 4, row: 6 }, Coord { column: 6, row: 4 }, Coord { column: 4, row: 5 }, Coord { column: 5, row: 4 }, Coord { column: 4, row: 4 }, ],
"Player below right should yield expected neighbor order"
);
board[Coord { column: 4, row: 4 }] = Tile::Block;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 6, row: 6 }, Coord { column: 5, row: 6 }, Coord { column: 6, row: 5 }, Coord { column: 4, row: 6 }, Coord { column: 6, row: 4 }, Coord { column: 4, row: 5 }, Coord { column: 5, row: 4 }, ],
"Player below right should yield expected neighbor order with blocking tiles"
);
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, false),
vec![
Coord { column: 6, row: 6 }, Coord { column: 5, row: 6 }, Coord { column: 6, row: 5 }, Coord { column: 4, row: 6 }, Coord { column: 6, row: 4 }, Coord { column: 4, row: 5 }, Coord { column: 5, row: 4 }, Coord { column: 4, row: 4 }, ],
"Player below right should yield expected neighbor order without checking for blocking tiles"
);
board[Coord { column: 4, row: 4 }] = Tile::Block;
board[Coord { column: 5, row: 4 }] = Tile::Block;
board[Coord { column: 6, row: 4 }] = Tile::Block;
board[Coord { column: 4, row: 5 }] = Tile::Block;
board[Coord { column: 6, row: 5 }] = Tile::Block;
board[Coord { column: 4, row: 6 }] = Tile::Block;
board[Coord { column: 5, row: 6 }] = Tile::Block;
board[Coord { column: 6, row: 6 }] = Tile::Block;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
Vec::new(),
"Player below right should yield no neighbor for a blocked board"
);
}
#[test]
fn get_walkable_coords_above_right_test() {
let mut board = Board::new([[Tile::Empty; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord { column: 5, row: 5 };
let player = Coord { column: 7, row: 3 };
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 6, row: 4 }, Coord { column: 5, row: 4 }, Coord { column: 6, row: 5 }, Coord { column: 4, row: 4 }, Coord { column: 6, row: 6 }, Coord { column: 4, row: 5 }, Coord { column: 5, row: 6 }, Coord { column: 4, row: 6 }, ],
"Player above right should yield expected neighbor order"
);
board[Coord { column: 6, row: 4 }] = Tile::Block;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 5, row: 4 }, Coord { column: 6, row: 5 }, Coord { column: 4, row: 4 }, Coord { column: 6, row: 6 }, Coord { column: 4, row: 5 }, Coord { column: 5, row: 6 }, Coord { column: 4, row: 6 }, ],
"Player above right should yield expected neighbor order with blocking tiles"
);
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, false),
vec![
Coord { column: 6, row: 4 }, Coord { column: 5, row: 4 }, Coord { column: 6, row: 5 }, Coord { column: 4, row: 4 }, Coord { column: 6, row: 6 }, Coord { column: 4, row: 5 }, Coord { column: 5, row: 6 }, Coord { column: 4, row: 6 }, ],
"Player above right should yield expected neighbor order without checking for blocking tiles"
);
board[Coord { column: 4, row: 4 }] = Tile::Block;
board[Coord { column: 5, row: 4 }] = Tile::Block;
board[Coord { column: 6, row: 4 }] = Tile::Block;
board[Coord { column: 4, row: 5 }] = Tile::Block;
board[Coord { column: 6, row: 5 }] = Tile::Block;
board[Coord { column: 4, row: 6 }] = Tile::Block;
board[Coord { column: 5, row: 6 }] = Tile::Block;
board[Coord { column: 6, row: 6 }] = Tile::Block;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
Vec::new(),
"Player above right should yield no neighbor for a blocked board"
);
}
#[test]
fn get_walkable_coords_below_left_test() {
let mut board = Board::new([[Tile::Empty; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord { column: 5, row: 5 };
let player = Coord { column: 3, row: 7 };
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 4, row: 6 }, Coord { column: 4, row: 5 }, Coord { column: 5, row: 6 }, Coord { column: 4, row: 4 }, Coord { column: 6, row: 6 }, Coord { column: 6, row: 5 }, Coord { column: 5, row: 4 }, Coord { column: 6, row: 4 }, ],
"Player below left should yield expected neighbor order"
);
board[Coord { column: 4, row: 6 }] = Tile::Block;
board[Coord { column: 6, row: 6 }] = Tile::Block;
board[Coord { column: 6, row: 4 }] = Tile::Block;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 4, row: 5 }, Coord { column: 5, row: 6 }, Coord { column: 4, row: 4 }, Coord { column: 6, row: 5 }, Coord { column: 5, row: 4 }, ],
"Player below left should yield expected neighbor order with blocking tiles"
);
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, false),
vec![
Coord { column: 4, row: 6 }, Coord { column: 4, row: 5 }, Coord { column: 5, row: 6 }, Coord { column: 4, row: 4 }, Coord { column: 6, row: 6 }, Coord { column: 6, row: 5 }, Coord { column: 5, row: 4 }, Coord { column: 6, row: 4 }, ],
"Player below left should yield expected neighbor order without checking for blocking tiles"
);
board[Coord { column: 4, row: 4 }] = Tile::Block;
board[Coord { column: 5, row: 4 }] = Tile::Block;
board[Coord { column: 6, row: 4 }] = Tile::Block;
board[Coord { column: 4, row: 5 }] = Tile::Block;
board[Coord { column: 6, row: 5 }] = Tile::Block;
board[Coord { column: 4, row: 6 }] = Tile::Block;
board[Coord { column: 5, row: 6 }] = Tile::Block;
board[Coord { column: 6, row: 6 }] = Tile::Block;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
Vec::new(),
"Player below left should yield no neighbor for a blocked board"
);
}
#[test]
fn get_walkable_coords_above_left_test() {
let mut board = Board::new([[Tile::Empty; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord { column: 5, row: 5 };
let player = Coord { column: 3, row: 3 };
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 4, row: 4 }, Coord { column: 4, row: 5 }, Coord { column: 5, row: 4 }, Coord { column: 4, row: 6 }, Coord { column: 6, row: 4 }, Coord { column: 5, row: 6 }, Coord { column: 6, row: 5 }, Coord { column: 6, row: 6 }, ],
"Player above left should yield expected neighbor order"
);
board[Coord { column: 4, row: 5 }] = Tile::StaticBlock;
board[Coord { column: 4, row: 6 }] = Tile::StaticBlock;
board[Coord { column: 5, row: 6 }] = Tile::StaticBlock;
board[Coord { column: 6, row: 6 }] = Tile::StaticBlock;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 4, row: 4 }, Coord { column: 5, row: 4 }, Coord { column: 6, row: 4 }, Coord { column: 6, row: 5 }, ],
"Player above left should yield expected neighbor order with blocking tiles"
);
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, false),
vec![
Coord { column: 4, row: 4 }, Coord { column: 4, row: 5 }, Coord { column: 5, row: 4 }, Coord { column: 4, row: 6 }, Coord { column: 6, row: 4 }, Coord { column: 5, row: 6 }, Coord { column: 6, row: 5 }, Coord { column: 6, row: 6 }, ],
"Player above left should yield expected neighbor order without checking for blocking tiles"
);
board[Coord { column: 4, row: 4 }] = Tile::Block;
board[Coord { column: 5, row: 4 }] = Tile::Block;
board[Coord { column: 6, row: 4 }] = Tile::Block;
board[Coord { column: 4, row: 5 }] = Tile::Block;
board[Coord { column: 6, row: 5 }] = Tile::Block;
board[Coord { column: 4, row: 6 }] = Tile::Block;
board[Coord { column: 5, row: 6 }] = Tile::Block;
board[Coord { column: 6, row: 6 }] = Tile::Block;
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
Vec::new(),
"Player above left should yield no neighbor for a blocked board"
);
}
#[test]
#[should_panic]
fn get_walkable_coords_same_pos_test() {
let board = Board::new([[Tile::Empty; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord { column: 5, row: 5 };
let player = Coord { column: 5, row: 5 };
DummyBeast::get_walkable_coords(&board, &pos, &player, true);
}
#[test]
fn get_walkable_coords_without_tile_check_test() {
let board = Board::new([[Tile::Block; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord { column: 5, row: 5 };
let player = Coord { column: 5, row: 7 };
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, false),
vec![
Coord { column: 5, row: 6 },
Coord { column: 4, row: 6 },
Coord { column: 6, row: 6 },
Coord { column: 4, row: 5 },
Coord { column: 6, row: 5 },
Coord { column: 4, row: 4 },
Coord { column: 6, row: 4 },
Coord { column: 5, row: 4 },
],
"When check_tiles is false, all neighbor coordinates are returned regardless of block state"
);
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
Vec::new(),
"When check_tiles is true, no neighbor coordinates are returned for a full board"
);
}
#[test]
fn get_walkable_coords_boundary_top_left_test() {
let board = Board::new([[Tile::Empty; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord { column: 0, row: 0 };
let player = Coord { column: 0, row: 1 };
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord { column: 0, row: 1 }, Coord { column: 1, row: 1 }, Coord { column: 0, row: 0 }, Coord { column: 1, row: 0 }, ],
"Boundary test: Top-left corner should properly clamp coordinates without duplicates"
);
}
#[test]
fn get_walkable_coords_boundary_top_right_test() {
let board = Board::new([[Tile::Empty; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord {
column: BOARD_WIDTH - 1,
row: 0,
};
let player = Coord {
column: BOARD_WIDTH - 1,
row: 1,
};
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord {
column: BOARD_WIDTH - 1,
row: 1,
},
Coord {
column: BOARD_WIDTH - 2,
row: 1,
},
Coord {
column: BOARD_WIDTH - 2,
row: 0,
},
Coord {
column: BOARD_WIDTH - 1,
row: 0,
},
],
"Boundary test: Top-right corner should properly clamp coordinates and remove duplicates"
);
}
#[test]
fn get_walkable_coords_boundary_bottom_left_test() {
let board = Board::new([[Tile::Empty; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord {
column: 0,
row: BOARD_HEIGHT - 1,
};
let player = Coord {
column: 0,
row: BOARD_HEIGHT - 2,
};
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord {
column: 0,
row: BOARD_HEIGHT - 2,
},
Coord {
column: 1,
row: BOARD_HEIGHT - 2,
},
Coord {
column: 0,
row: BOARD_HEIGHT - 1,
},
Coord {
column: 1,
row: BOARD_HEIGHT - 1,
},
],
"Boundary test: Bottom-left corner should properly clamp coordinates and remove duplicates"
);
}
#[test]
fn get_walkable_coords_boundary_bottom_right_test() {
let board = Board::new([[Tile::Empty; BOARD_WIDTH]; BOARD_HEIGHT]);
let pos = Coord {
column: BOARD_WIDTH - 1,
row: BOARD_HEIGHT - 1,
};
let player = Coord {
column: BOARD_WIDTH - 1,
row: BOARD_HEIGHT - 2,
};
assert_eq!(
DummyBeast::get_walkable_coords(&board, &pos, &player, true),
vec![
Coord {
column: BOARD_WIDTH - 1,
row: BOARD_HEIGHT - 2,
},
Coord {
column: BOARD_WIDTH - 2,
row: BOARD_HEIGHT - 2,
},
Coord {
column: BOARD_WIDTH - 2,
row: BOARD_HEIGHT - 1,
},
Coord {
column: BOARD_WIDTH - 1,
row: BOARD_HEIGHT - 1,
},
],
"Boundary test: Bottom-right corner should properly clamp coordinates and remove duplicates"
);
}
#[test]
fn heuristic_test() {
let a = Coord { column: 3, row: 4 };
let b = Coord { column: 6, row: 8 };
assert_eq!(DummyBeast::heuristic(&a, &b), 4, "We have calculated the heuristic distance between two coordinates");
let a = Coord { column: 5, row: 5 };
let b = Coord { column: 2, row: 7 };
assert_eq!(DummyBeast::heuristic(&a, &b), 3, "We have calculated the heuristic distance between two coordinates");
}
#[test]
fn reconstruct_path_test() {
let mut came_from = HashMap::new();
let start = Coord { column: 1, row: 1 };
let mid1 = Coord { column: 2, row: 1 };
let mid2 = Coord { column: 3, row: 2 };
let goal = Coord { column: 3, row: 3 };
came_from.insert(mid1, start);
came_from.insert(mid2, mid1);
came_from.insert(goal, mid2);
let path = DummyBeast::reconstruct_path(&came_from, goal);
assert_eq!(path, vec![start, mid1, mid2, goal], "We have reconstructed the path from start to goal");
}
}