use super::{Tessellation, TessellationError, validate_and_apply_delta};
use crate::game_board::BoardTransform;
use crate::{Direction, TileShape};
pub static KNOWN_DIRECTIONS: [Direction; 6] = [
Direction::Left,
Direction::Right,
Direction::NorthEast,
Direction::NorthWest,
Direction::SouthEast,
Direction::SouthWest,
];
pub static TILE_SHAPES: [TileShape; 1] = [TileShape::Hexagon];
pub const ROTATION_STEP_DEGREES: u16 = 60;
pub const fn tile_shape_from_pos() -> TileShape { TileShape::Hexagon }
pub const fn is_rigid_translation(_dx: i64, dy: i64) -> bool { dy.rem_euclid(2) == 0 }
fn mirror_vertical(x: i64, y: i64) -> (i64, i64) { (-x - y.rem_euclid(2), y) }
fn rotate_clockwise(x: i64, y: i64) -> (i64, i64) {
let q = x - (y - y.rem_euclid(2)) / 2;
let cube_y = -q - y;
let cube_z = y;
let rotated_x = -cube_z;
let rotated_z = -cube_y;
let row = rotated_z;
let column = rotated_x + (row - row.rem_euclid(2)) / 2;
(column, row)
}
pub fn transformed_raw(transform: BoardTransform, x: i64, y: i64) -> (i64, i64) {
match transform {
BoardTransform::MirrorHorizontal => (x, -y),
BoardTransform::MirrorVertical => mirror_vertical(x, y),
BoardTransform::RotateClockwise => rotate_clockwise(x, y),
BoardTransform::RotateCounterClockwise => {
let (x, y) = mirror_vertical(x, y);
let (x, y) = rotate_clockwise(x, y);
mirror_vertical(x, y)
}
}
}
pub fn neighbor_checked(
x: usize, y: usize, to: Direction, width: usize, height: usize,
) -> Result<(usize, usize), TessellationError> {
let dx: i8;
let dy: i8;
match to {
Direction::Left => {
dx = -1;
dy = 0;
}
Direction::Right => {
dx = 1;
dy = 0;
}
Direction::NorthEast => {
dx = if y % 2 == 1 { 1 } else { 0 };
dy = -1;
}
Direction::NorthWest => {
dx = -(if ((y + 1) % 2) == 1 { 1 } else { 0 });
dy = -1;
}
Direction::SouthEast => {
dx = if y % 2 == 1 { 1 } else { 0 };
dy = 1;
}
Direction::SouthWest => {
dx = -(if ((y + 1) % 2) == 1 { 1 } else { 0 });
dy = 1;
}
_ => {
return Err(TessellationError::IllegalDirection(Tessellation::Hexoban, to));
}
}
validate_and_apply_delta(x, y, to, dx, dy, width, height)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Size2D;
mod test_transforms {
use super::*;
type Expectation = ((i64, i64), [(i64, i64); 4]);
const TRANSFORMS: [BoardTransform; 4] = [
BoardTransform::MirrorHorizontal,
BoardTransform::MirrorVertical,
BoardTransform::RotateClockwise,
BoardTransform::RotateCounterClockwise,
];
#[rustfmt::skip]
#[test]
fn it_maps_the_first_two_rows_under_every_transform() {
let expectations: [Expectation; 8] = [
((0, 0), [(0, 0), ( 0, 0), ( 0, 0), (0, 0)]),
((1, 0), [(1, 0), (-1, 0), ( 0, 1), (0, -1)]),
((2, 0), [(2, 0), (-2, 0), ( 1, 2), (1, -2)]),
((3, 0), [(3, 0), (-3, 0), ( 1, 3), (1, -3)]),
((0, 1), [(0, -1), (-1, 1), (-1, 1), (1, 0)]),
((1, 1), [(1, -1), (-2, 1), ( 0, 2), (1, -1)]),
((2, 1), [(2, -1), (-3, 1), ( 0, 3), (2, -2)]),
((3, 1), [(3, -1), (-4, 1), ( 1, 4), (2, -3)]),
];
for ((x, y), wanted) in expectations {
for (transform, want) in TRANSFORMS.iter().zip(wanted) {
assert_eq!(
transformed_raw(*transform, x, y), want,
"{transform:?} at ({x}, {y})"
);
}
}
}
#[rustfmt::skip]
#[test]
fn it_maps_negative_coordinates() {
let expectations: [Expectation; 8] = [
((0, -1), [( 0, 1), (-1, -1), ( 1, 0), (-1, -1)]),
((1, -1), [( 1, 1), (-2, -1), ( 1, 1), ( 0, -2)]),
((-1, 0), [(-1, 0), ( 1, 0), (-1, -1), (-1, 1)]),
((-1, -1), [(-1, 1), ( 0, -1), ( 0, -1), (-1, 0)]),
((-2, -1), [(-2, 1), ( 1, -1), ( 0, -2), (-2, 1)]),
((-1, -2), [(-1, 2), ( 1, -2), ( 1, -2), (-2, 0)]),
((-2, -2), [(-2, 2), ( 2, -2), ( 0, -3), (-3, 1)]),
((-3, -2), [(-3, 2), ( 3, -2), ( 0, -4), (-3, 2)]),
];
for ((x, y), wanted) in expectations {
for (transform, want) in TRANSFORMS.iter().zip(wanted) {
assert_eq!(
transformed_raw(*transform, x, y), want,
"{transform:?} at ({x}, {y})"
);
}
}
}
#[test]
fn it_returns_to_the_start_only_after_a_full_turn() {
let steps = 360 / usize::from(ROTATION_STEP_DEGREES);
assert_eq!(steps, 6);
let witness = (2_i64, 1_i64);
let mut point = witness;
for step in 1..steps {
point = transformed_raw(BoardTransform::RotateClockwise, point.0, point.1);
assert_ne!(point, witness, "home again after {step} of {steps} steps");
}
for y in -6_i64..=6 {
for x in -6_i64..=6 {
let mut point = (x, y);
for _ in 0..steps {
point = transformed_raw(BoardTransform::RotateClockwise, point.0, point.1);
}
assert_eq!(point, (x, y), "({x}, {y}) after a full turn");
}
}
}
#[test]
fn counter_clockwise_inverts_clockwise_and_mirrors_are_involutions() {
for y in -6_i64..=6 {
for x in -6_i64..=6 {
let clockwise = transformed_raw(BoardTransform::RotateClockwise, x, y);
assert_eq!(
transformed_raw(
BoardTransform::RotateCounterClockwise,
clockwise.0,
clockwise.1
),
(x, y),
"CCW . CW at ({x}, {y})"
);
for mirror in [
BoardTransform::MirrorHorizontal,
BoardTransform::MirrorVertical,
] {
let once = transformed_raw(mirror, x, y);
assert_eq!(
transformed_raw(mirror, once.0, once.1),
(x, y),
"{mirror:?} twice at ({x}, {y})"
);
}
}
}
}
#[test]
fn it_reports_which_translations_are_rigid() {
let rigid = (-3_i64..=3)
.flat_map(|dy| (-3_i64..=3).map(move |dx| (dx, dy)))
.filter(|(dx, dy)| is_rigid_translation(*dx, *dy))
.count();
assert_eq!(rigid, 21);
}
#[test]
fn it_mirrors_odd_and_even_rows_differently() {
assert_eq!(
transformed_raw(BoardTransform::MirrorVertical, 3, 0),
(-3, 0),
"an even row mirrors to -x"
);
assert_eq!(
transformed_raw(BoardTransform::MirrorVertical, 3, 1),
(-4, 1),
"an odd row mirrors half a tile further, to -x - 1"
);
for mirror in [
BoardTransform::MirrorHorizontal,
BoardTransform::MirrorVertical,
] {
for y in -8_i64..=8 {
for x in -8_i64..=8 {
let (_, ty) = transformed_raw(mirror, x, y);
assert_eq!(
y.rem_euclid(2),
ty.rem_euclid(2),
"{mirror:?} changed the row parity at ({x}, {y})"
);
}
}
}
}
#[test]
fn it_rotates_every_direction_by_one_step() {
let relabelled = [
(Direction::Right, Direction::SouthEast),
(Direction::SouthEast, Direction::SouthWest),
(Direction::SouthWest, Direction::Left),
(Direction::Left, Direction::NorthWest),
(Direction::NorthWest, Direction::NorthEast),
(Direction::NorthEast, Direction::Right),
];
for y in 0_usize..4 {
for x in 0_usize..4 {
for (direction, rotated) in relabelled {
assert_rotated_direction(x, y, direction, rotated);
}
}
}
}
}
#[cfg(test)]
fn assert_rotated_direction(x: usize, y: usize, direction: Direction, rotated: Direction) {
const ORIGIN: usize = 64;
const SHIFT: i64 = 128;
const SIZE: usize = 256;
let turned = |(cx, cy): (usize, usize)| {
let (tx, ty) = transformed_raw(
BoardTransform::RotateClockwise,
i64::try_from(cx).unwrap(),
i64::try_from(cy).unwrap(),
);
(
usize::try_from(tx + SHIFT).unwrap(),
usize::try_from(ty + SHIFT).unwrap(),
)
};
let (column, row) = (x + ORIGIN, y + ORIGIN);
let neighbor = neighbor_checked(column, row, direction, SIZE, SIZE)
.expect("the source neighbour is on board");
let source = turned((column, row));
assert_eq!(
neighbor_checked(source.0, source.1, rotated, SIZE, SIZE),
Ok(turned(neighbor)),
"a clockwise turn should carry {direction:?} onto {rotated:?}, at ({column}, {row})"
);
}
#[rustfmt::skip]
#[test]
fn test_top_left() {
let size = Size2D::new(5, 5);
let width = size.width;
let height = size.height;
let row: usize = 0;
let column: usize = 0;
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Ok((1, 0))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Ok((0, 1))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_top_right_column_odd() {
let size = Size2D::new(4, 4);
let width = size.width;
let height = size.height;
let row: usize = 0;
let column: usize = 3;
assert_eq!(column % 2, 1);
let expectations = [
(Direction::Left , Ok((2, 0))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Ok((2, 1))),
(Direction::SouthEast, Ok((3, 1))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_top_right_column_even() {
let size = Size2D::new(5, 5);
let width = size.width;
let height = size.height;
let row: usize = 0;
let column: usize = 4;
assert_eq!(column % 2, 0);
let expectations = [
(Direction::Left , Ok((3, 0))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Ok((3, 1))),
(Direction::SouthEast, Ok((4, 1))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_bottom_left_row_odd() {
let size = Size2D::new(4, 4);
let width = size.width;
let height = size.height;
let row: usize = 3;
let column: usize = 0;
assert_eq!(row % 2, 1);
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Ok((1, 3))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((0, 2))),
(Direction::NorthEast, Ok((1, 2))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_bottom_left_row_even() {
let size = Size2D::new(5, 5);
let width = size.width;
let height = size.height;
let row: usize = 4;
let column: usize = 0;
assert_eq!(row % 2, 0);
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Ok((1, 4))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Ok((0, 3))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_bottom_right_row_even_column_even() {
let size = Size2D::new(5, 5);
let width = size.width;
let height = size.height;
let row: usize = 4;
let column: usize = 4;
assert_eq!(row % 2, 0);
assert_eq!(column % 2, 0);
let expectations = [
(Direction::Left , Ok((3, 4))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((3, 3))),
(Direction::NorthEast, Ok((4, 3))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_bottom_right_row_even_column_odd() {
let size = Size2D::new(4, 5);
let width = size.width;
let height = size.height;
let row: usize = 4;
let column: usize = 3;
assert_eq!(row % 2, 0);
assert_eq!(column % 2, 1);
let expectations = [
(Direction::Left , Ok((2, 4))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((2, 3))),
(Direction::NorthEast, Ok((3, 3))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_bottom_right_row_odd_column_even() {
let size = Size2D::new(5, 4);
let width = size.width;
let height = size.height;
let row: usize = 3;
let column: usize = 4;
assert_eq!(row % 2, 1);
assert_eq!(column % 2, 0);
let expectations = [
(Direction::Left , Ok((3, 3))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((4, 2))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_bottom_right_row_odd_column_odd() {
let size = Size2D::new(4, 4);
let width = size.width;
let height = size.height;
let row: usize = 3;
let column: usize = 3;
assert_eq!(row % 2, 1);
assert_eq!(column % 2, 1);
let expectations = [
(Direction::Left , Ok((2, 3))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((3, 2))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_top_column_odd() {
let size = Size2D::new(10, 10);
let width = size.width;
let height = size.height;
let row: usize = 0;
let column: usize = 3;
assert_eq!(column % 2, 1);
let expectations = [
(Direction::Left , Ok((2, 0))),
(Direction::Right , Ok((4, 0))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Ok((2, 1))),
(Direction::SouthEast, Ok((3, 1))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_top_column_even() {
let size = Size2D::new(10, 10);
let width = size.width;
let height = size.height;
let row: usize = 0;
let column: usize = 4;
assert_eq!(column % 2, 0);
let expectations = [
(Direction::Left , Ok((3, 0))),
(Direction::Right , Ok((5, 0))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Ok((3, 1))),
(Direction::SouthEast, Ok((4, 1))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_bottom_column_odd_row_even() {
let size = Size2D::new(10, 5);
let width = size.width;
let height = size.height;
let row: usize = 4;
let column: usize = 3;
assert_eq!(column % 2, 1);
assert_eq!(row % 2, 0);
let expectations = [
(Direction::Left , Ok((2, 4))),
(Direction::Right , Ok((4, 4))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((2, 3))),
(Direction::NorthEast, Ok((3, 3))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_bottom_column_even_row_even() {
let size = Size2D::new(10, 5);
let width = size.width;
let height = size.height;
let row: usize = 4;
let column: usize = 4;
assert_eq!(column % 2, 0);
assert_eq!(row % 2, 0);
let expectations = [
(Direction::Left , Ok((3, 4))),
(Direction::Right , Ok((5, 4))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((3, 3))),
(Direction::NorthEast, Ok((4, 3))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_bottom_column_odd_row_odd() {
let size = Size2D::new(10, 4);
let width = size.width;
let height = size.height;
let row: usize = 3;
let column: usize = 3;
assert_eq!(column % 2, 1);
assert_eq!(row % 2, 1);
let expectations = [
(Direction::Left , Ok((2, 3))),
(Direction::Right , Ok((4, 3))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((3, 2))),
(Direction::NorthEast, Ok((4, 2))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_bottom_column_even_row_odd() {
let size = Size2D::new(10, 4);
let width = size.width;
let height = size.height;
let row: usize = 3;
let column: usize = 4;
assert_eq!(column % 2, 0);
assert_eq!(row % 2, 1);
let expectations = [
(Direction::Left , Ok((3, 3))),
(Direction::Right , Ok((5, 3))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((4, 2))),
(Direction::NorthEast, Ok((5, 2))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_left_row_odd() {
let size = Size2D::new(10, 10);
let width = size.width;
let height = size.height;
let row: usize = 3;
let column: usize = 0;
assert_eq!(row % 2, 1);
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Ok((1, 3))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((0, 2))),
(Direction::NorthEast, Ok((1, 2))),
(Direction::SouthWest, Ok((0, 4))),
(Direction::SouthEast, Ok((1, 4))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_left_row_even() {
let size = Size2D::new(10, 10);
let width = size.width;
let height = size.height;
let row: usize = 2;
let column: usize = 0;
assert_eq!(row % 2, 0);
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Ok((1, 2))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Ok((0, 1))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Ok((0, 3))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_right_row_odd_column_odd() {
let size = Size2D::new(4, 10);
let width = size.width;
let height = size.height;
let row: usize = 3;
let column: usize = 3;
assert_eq!(row % 2, 1);
assert_eq!(column % 2, 1);
let expectations = [
(Direction::Left , Ok((2, 3))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((3, 2))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Ok((3, 4))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_right_row_even_column_odd() {
let size = Size2D::new(4, 10);
let width = size.width;
let height = size.height;
let row: usize = 2;
let column: usize = 3;
assert_eq!(row % 2, 0);
assert_eq!(column % 2, 1);
let expectations = [
(Direction::Left , Ok((2, 2))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((2, 1))),
(Direction::NorthEast, Ok((3, 1))),
(Direction::SouthWest, Ok((2, 3))),
(Direction::SouthEast, Ok((3, 3))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_right_row_odd_column_even() {
let size = Size2D::new(5, 10);
let width = size.width;
let height = size.height;
let row: usize = 3;
let column: usize = 4;
assert_eq!(row % 2, 1);
assert_eq!(column % 2, 0);
let expectations = [
(Direction::Left , Ok((3, 3))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((4, 2))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Ok((4, 4))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_right_row_even_column_even() {
let size = Size2D::new(5, 10);
let width = size.width;
let height = size.height;
let row: usize = 2;
let column: usize = 4;
assert_eq!(row % 2, 0);
assert_eq!(column % 2, 0);
let expectations = [
(Direction::Left , Ok((3, 2))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((3, 1))),
(Direction::NorthEast, Ok((4, 1))),
(Direction::SouthWest, Ok((3, 3))),
(Direction::SouthEast, Ok((4, 3))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_row_odd_column_odd() {
let size = Size2D::new(10, 10);
let width = size.width;
let height = size.height;
let row: usize = 3;
let column: usize = 3;
assert_eq!(row % 2, 1);
assert_eq!(column % 2, 1);
let expectations = [
(Direction::Left , Ok((2, 3))),
(Direction::Right , Ok((4, 3))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((3, 2))),
(Direction::NorthEast, Ok((4, 2))),
(Direction::SouthWest, Ok((3, 4))),
(Direction::SouthEast, Ok((4, 4))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_row_odd_column_even() {
let size = Size2D::new(10, 10);
let width = size.width;
let height = size.height;
let row: usize = 3;
let column: usize = 2;
assert_eq!(row % 2, 1);
assert_eq!(column % 2, 0);
let expectations = [
(Direction::Left , Ok((1, 3))),
(Direction::Right , Ok((3, 3))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((2, 2))),
(Direction::NorthEast, Ok((3, 2))),
(Direction::SouthWest, Ok((2, 4))),
(Direction::SouthEast, Ok((3, 4))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_row_even_column_odd() {
let size = Size2D::new(10, 10);
let width = size.width;
let height = size.height;
let row: usize = 2;
let column: usize = 3;
assert_eq!(row % 2, 0);
assert_eq!(column % 2, 1);
let expectations = [
(Direction::Left , Ok((2, 2))),
(Direction::Right , Ok((4, 2))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((2, 1))),
(Direction::NorthEast, Ok((3, 1))),
(Direction::SouthWest, Ok((2, 3))),
(Direction::SouthEast, Ok((3, 3))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_middle_row_even_column_even() {
let size = Size2D::new(10, 10);
let width = size.width;
let height = size.height;
let row: usize = 2;
let column: usize = 2;
assert_eq!(row % 2, 0);
assert_eq!(column % 2, 0);
let expectations = [
(Direction::Left , Ok((1, 2))),
(Direction::Right , Ok((3, 2))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((1, 1))),
(Direction::NorthEast, Ok((2, 1))),
(Direction::SouthWest, Ok((1, 3))),
(Direction::SouthEast, Ok((2, 3))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_board1x1() {
let size = Size2D::new(1, 1);
let width = size.width;
let height = size.height;
let row: usize = 0;
let column: usize = 0;
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_board2x1_left() {
let size = Size2D::new(2, 1);
let width = size.width;
let height = size.height;
let row: usize = 0;
let column: usize = 0;
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Ok((1, 0))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_board2x1_right() {
let size = Size2D::new(2, 1);
let width = size.width;
let height = size.height;
let row: usize = 0;
let column: usize = 1;
let expectations = [
(Direction::Left , Ok((0, 0))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_board3x1_right() {
let size = Size2D::new(3, 1);
let width = size.width;
let height = size.height;
let row: usize = 0;
let column: usize = 2;
let expectations = [
(Direction::Left , Ok((1, 0))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_1x2_top() {
let size = Size2D::new(1, 2);
let width = size.width;
let height = size.height;
let row: usize = 0;
let column: usize = 0;
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Ok((0, 1))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_1x2_bottom() {
let size = Size2D::new(1, 2);
let width = size.width;
let height = size.height;
let row: usize = 1;
let column: usize = 0;
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Ok((0, 0))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
#[rustfmt::skip]
#[test]
fn test_1x3_bottom() {
let size = Size2D::new(1, 3);
let width = size.width;
let height = size.height;
let row: usize = 2;
let column: usize = 0;
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Hexoban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Ok((0, 1))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Err(TessellationError::OffBoard(column, row, Direction::SouthEast))),
];
for (direction, result) in &expectations {
assert_eq!(
neighbor_checked(column, row, *direction, width, height),
*result
);
}
}
}