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; 2] = [TileShape::TriangleUp, TileShape::TriangleDown];
pub const ROTATION_STEP_DEGREES: u16 = 60;
#[inline]
pub const fn tile_shape_from_pos(x: usize, y: usize) -> TileShape {
let parity = (x + (y % 2)) % 2;
if parity == 0 {
TileShape::TriangleDown
} else {
TileShape::TriangleUp
}
}
pub const fn is_rigid_translation(dx: i64, dy: i64) -> bool { (dx + dy).rem_euclid(2) == 0 }
fn mirror_vertical(x: i64, y: i64) -> (i64, i64) { (-x - 2, y) }
fn rotate_clockwise(x: i64, y: i64) -> (i64, i64) {
let parity = y.rem_euclid(2);
let transformed_x = (x - parity).div_euclid(2) - (3 * y).div_euclid(2) - 1;
let transformed_y = (x + 1 - parity).div_euclid(2) + (y + 1).div_euclid(2);
(transformed_x, transformed_y)
}
pub fn transformed_raw(transform: BoardTransform, x: i64, y: i64) -> (i64, i64) {
match transform {
BoardTransform::MirrorHorizontal => (x, -y - 1),
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 triangle_down: bool = tile_shape_from_pos(x, y) == TileShape::TriangleDown;
let dx: i8;
let dy: i8;
match to {
Direction::Left => {
dy = 0;
dx = -1;
}
Direction::Right => {
dy = 0;
dx = 1;
}
Direction::NorthEast => {
if triangle_down {
dy = -1;
dx = 0;
} else {
dy = 0;
dx = 1;
}
}
Direction::NorthWest => {
if triangle_down {
dy = -1;
dx = 0;
} else {
dy = 0;
dx = -1;
}
}
Direction::SouthEast => {
if triangle_down {
dy = 0;
dx = 1;
} else {
dy = 1;
dx = 0;
}
}
Direction::SouthWest => {
if triangle_down {
dy = 0;
dx = -1;
} else {
dy = 1;
dx = 0;
}
}
_ => {
return Err(TessellationError::IllegalDirection(Tessellation::Trioban, 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, -1), (-2, 0), (-1, 0), (0, -1)]),
((1, 0), [(1, -1), (-3, 0), (-1, 1), (1, -1)]),
((2, 0), [(2, -1), (-4, 0), ( 0, 1), (1, -2)]),
((3, 0), [(3, -1), (-5, 0), ( 0, 2), (2, -2)]),
((0, 1), [(0, -2), (-2, 1), (-3, 1), (2, 0)]),
((1, 1), [(1, -2), (-3, 1), (-2, 1), (2, -1)]),
((2, 1), [(2, -2), (-4, 1), (-2, 2), (3, -1)]),
((3, 1), [(3, -2), (-5, 1), (-1, 2), (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})"
);
}
}
}
#[rustfmt::skip]
#[test]
fn it_maps_negative_coordinates() {
let expectations: [Expectation; 8] = [
((0, -1), [( 0, 0), (-2, -1), ( 0, 0), (-1, -1)]),
((1, -1), [( 1, 0), (-3, -1), ( 1, 0), (-1, -2)]),
((-1, 0), [(-1, -1), (-1, 0), (-2, 0), ( 0, 0)]),
((-1, -1), [(-1, 0), (-1, -1), ( 0, -1), (-2, -1)]),
((-2, -1), [(-2, 0), ( 0, -1), (-1, -1), (-2, 0)]),
((-1, -2), [(-1, 1), (-1, -2), ( 1, -1), (-3, -1)]),
((-2, -2), [(-2, 1), ( 0, -2), ( 1, -2), (-4, -1)]),
((-3, -2), [(-3, 1), ( 1, -2), ( 0, -2), (-4, 0)]),
];
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, 25);
}
#[test]
fn it_flips_triangle_orientation_under_every_transform_but_the_vertical_mirror() {
let flips = [
(BoardTransform::MirrorHorizontal, true),
(BoardTransform::MirrorVertical, false),
(BoardTransform::RotateClockwise, true),
(BoardTransform::RotateCounterClockwise, true),
];
for (transform, should_flip) in flips {
for y in -8_i64..=8 {
for x in -8_i64..=8 {
let (tx, ty) = transformed_raw(transform, x, y);
let flipped = (x + y).rem_euclid(2) != (tx + ty).rem_euclid(2);
assert_eq!(
flipped, should_flip,
"{transform:?} at ({x}, {y}): orientation flip should be {should_flip}"
);
}
}
}
}
#[test]
fn a_rotation_carries_neighbours_onto_neighbours() {
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(),
)
};
for y in 0_usize..4 {
for x in 0_usize..4 {
let (column, row) = (x + ORIGIN, y + ORIGIN);
let source = turned((column, row));
for direction in KNOWN_DIRECTIONS {
let Ok(neighbor) = neighbor_checked(column, row, direction, SIZE, SIZE)
else {
continue;
};
let image = turned(neighbor);
let reachable = KNOWN_DIRECTIONS.iter().any(|candidate| {
neighbor_checked(source.0, source.1, *candidate, SIZE, SIZE)
== Ok(image)
});
assert!(
reachable,
"the image of the {direction:?} neighbour of ({column}, {row}) is not \
adjacent to the image"
);
}
}
}
}
}
fn triangle_points_down(x: usize, y: usize) -> bool {
tile_shape_from_pos(x, y) == TileShape::TriangleDown
}
#[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;
assert!(triangle_points_down(column, row));
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Ok((1, 0))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, 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((1, 0))),
];
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);
assert!(!triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((2, 0))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((2, 0))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Ok((3, 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);
assert!(triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((3, 0))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Ok((3, 0))),
(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_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);
assert!(!triangle_points_down(column, row));
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Ok((1, 3))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Ok((1, 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_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);
assert!(triangle_points_down(column, row));
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Ok((1, 4))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((0, 3))),
(Direction::NorthEast, Ok((0, 3))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(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_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);
assert!(triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((3, 4))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((4, 3))),
(Direction::NorthEast, Ok((4, 3))),
(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_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);
assert!(!triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((2, 4))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((2, 4))),
(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_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);
assert!(!triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((3, 3))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((3, 3))),
(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);
assert!(triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((2, 3))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((3, 2))),
(Direction::NorthEast, Ok((3, 2))),
(Direction::SouthWest, Ok((2, 3))),
(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);
assert!(!triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((2, 0))),
(Direction::Right , Ok((4, 0))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((2, 0))),
(Direction::NorthEast, Ok((4, 0))),
(Direction::SouthWest, Ok((3, 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);
assert!(triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((3, 0))),
(Direction::Right , Ok((5, 0))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Ok((3, 0))),
(Direction::SouthEast, Ok((5, 0))),
];
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);
assert!(!triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((2, 4))),
(Direction::Right , Ok((4, 4))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((2, 4))),
(Direction::NorthEast, Ok((4, 4))),
(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);
assert!(triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((3, 4))),
(Direction::Right , Ok((5, 4))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((4, 3))),
(Direction::NorthEast, Ok((4, 3))),
(Direction::SouthWest, Ok((3, 4))),
(Direction::SouthEast, Ok((5, 4))),
];
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);
assert!(triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((2, 3))),
(Direction::Right , Ok((4, 3))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((3, 2))),
(Direction::NorthEast, Ok((3, 2))),
(Direction::SouthWest, Ok((2, 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_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);
assert!(!triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((3, 3))),
(Direction::Right , Ok((5, 3))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((3, 3))),
(Direction::NorthEast, Ok((5, 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_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);
assert!(!triangle_points_down(column, row));
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Ok((1, 3))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Ok((1, 3))),
(Direction::SouthWest, Ok((0, 4))),
(Direction::SouthEast, Ok((0, 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);
assert!(triangle_points_down(column, row));
let expectations = [
(Direction::Left , Err(TessellationError::OffBoard(column, row, Direction::Left))),
(Direction::Right , Ok((1, 2))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((0, 1))),
(Direction::NorthEast, Ok((0, 1))),
(Direction::SouthWest, Err(TessellationError::OffBoard(column, row, Direction::SouthWest))),
(Direction::SouthEast, Ok((1, 2))),
];
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);
assert!(triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((2, 3))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((3, 2))),
(Direction::NorthEast, Ok((3, 2))),
(Direction::SouthWest, Ok((2, 3))),
(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);
assert!(!triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((2, 2))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((2, 2))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Ok((3, 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);
assert!(!triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((3, 3))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((3, 3))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Ok((4, 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_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);
assert!(triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((3, 2))),
(Direction::Right , Err(TessellationError::OffBoard(column, row, Direction::Right))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((4, 1))),
(Direction::NorthEast, Ok((4, 1))),
(Direction::SouthWest, Ok((3, 2))),
(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_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);
assert!(triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((2, 3))),
(Direction::Right , Ok((4, 3))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((3, 2))),
(Direction::NorthEast, Ok((3, 2))),
(Direction::SouthWest, Ok((2, 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_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);
assert!(!triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((1, 3))),
(Direction::Right , Ok((3, 3))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((1, 3))),
(Direction::NorthEast, Ok((3, 3))),
(Direction::SouthWest, Ok((2, 4))),
(Direction::SouthEast, Ok((2, 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);
assert!(!triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((2, 2))),
(Direction::Right , Ok((4, 2))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((2, 2))),
(Direction::NorthEast, Ok((4, 2))),
(Direction::SouthWest, Ok((3, 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);
assert!(triangle_points_down(column, row));
let expectations = [
(Direction::Left , Ok((1, 2))),
(Direction::Right , Ok((3, 2))),
(Direction::Up , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((2, 1))),
(Direction::NorthEast, Ok((2, 1))),
(Direction::SouthWest, Ok((1, 2))),
(Direction::SouthEast, Ok((3, 2))),
];
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::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, 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::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, 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((1, 0))),
];
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::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, 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_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::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Err(TessellationError::OffBoard(column, row, Direction::NorthWest))),
(Direction::NorthEast, Err(TessellationError::OffBoard(column, row, Direction::NorthEast))),
(Direction::SouthWest, Ok((1, 0))),
(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::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, 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_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::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, 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_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::Trioban, Direction::Up))),
(Direction::Down , Err(TessellationError::IllegalDirection(Tessellation::Trioban, Direction::Down))),
(Direction::NorthWest, Ok((0, 1))),
(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
);
}
}
}