901 lines
22 KiB
Rust
901 lines
22 KiB
Rust
//! TODO: module doc :v
|
|
|
|
use bevy::ecs::component::Component;
|
|
|
|
/// Value for the "sub tiles" inside a room tile
|
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
|
pub enum Cell {
|
|
#[default]
|
|
Empty,
|
|
NW,
|
|
NE,
|
|
SE,
|
|
SW,
|
|
Filled,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct Walls {
|
|
pub north: bool,
|
|
pub east: bool,
|
|
pub south: bool,
|
|
pub west: bool,
|
|
}
|
|
|
|
impl Walls {
|
|
pub fn new(north: bool, east: bool, south: bool, west: bool) -> Self {
|
|
Self {
|
|
north,
|
|
east,
|
|
south,
|
|
west,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum CutLine {
|
|
VertLeft,
|
|
VertRight,
|
|
HorizUpper,
|
|
HorizLower,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum FlipDir {
|
|
Vertical,
|
|
Horizontal,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum TransposeIndex {
|
|
First,
|
|
Second,
|
|
Third,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum TransposeSelection {
|
|
Column(TransposeIndex),
|
|
Row(TransposeIndex),
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum RotationDir {
|
|
Clockwise,
|
|
CounterClockwise,
|
|
}
|
|
|
|
/// An invidiual room, or "card" in the player's hand. The room may
|
|
/// *or may not* be valid, yet.
|
|
#[derive(Clone, Copy, Component, Debug, Default, PartialEq)]
|
|
pub struct Card {
|
|
pub cells: [Cell; 9],
|
|
pub nw: bool,
|
|
pub n: bool,
|
|
pub ne: bool,
|
|
pub w: bool,
|
|
pub e: bool,
|
|
pub sw: bool,
|
|
pub s: bool,
|
|
pub se: bool,
|
|
}
|
|
|
|
impl Card {
|
|
/// Produces a new card by stacking another on top of this one.
|
|
pub fn merge(self, top: Self) -> (Self, Option<Self>) {
|
|
let mut new_card = Self::default();
|
|
let mut doors: Option<Self> = None;
|
|
|
|
new_card.cells = std::iter::zip(&self.cells, &top.cells)
|
|
.map(|(lower, upper)| {
|
|
if (lower == &Cell::Filled || upper == &Cell::Filled)
|
|
|| (lower == &Cell::SW && upper == &Cell::NE)
|
|
|| (lower == &Cell::SE && upper == &Cell::NW)
|
|
|| (lower == &Cell::NE && upper == &Cell::SW)
|
|
|| (lower == &Cell::NW && upper == &Cell::SE)
|
|
{
|
|
Cell::Filled
|
|
} else if upper != &Cell::Empty {
|
|
*upper
|
|
} else {
|
|
*lower
|
|
}
|
|
})
|
|
.collect::<Vec<Cell>>()
|
|
.try_into()
|
|
.unwrap();
|
|
|
|
// Check for doors
|
|
macro_rules! check_door {
|
|
( $dir:ident, $index:literal ) => {
|
|
if self.$dir {
|
|
if top.cells[$index] == Cell::Empty || top.$dir {
|
|
new_card.$dir = true;
|
|
} else {
|
|
doors.get_or_insert_default().$dir = true;
|
|
}
|
|
} else if top.$dir && new_card.cells[$index] != Cell::Empty {
|
|
new_card.$dir = true;
|
|
}
|
|
};
|
|
}
|
|
|
|
check_door!(nw, 0);
|
|
check_door!(n, 1);
|
|
check_door!(ne, 2);
|
|
check_door!(w, 3);
|
|
check_door!(e, 5);
|
|
check_door!(sw, 6);
|
|
check_door!(s, 7);
|
|
check_door!(se, 8);
|
|
|
|
(new_card, doors)
|
|
}
|
|
|
|
/// Cuts this Card on the given line. Returns two cards
|
|
pub fn cut(self, line: CutLine) -> (Self, Self) {
|
|
let is_cut: fn(usize, usize) -> bool = match line {
|
|
CutLine::VertLeft => |x, _| x > 0,
|
|
CutLine::VertRight => |x, _| x > 1,
|
|
CutLine::HorizUpper => |_, y| y > 0,
|
|
CutLine::HorizLower => |_, y| y > 1,
|
|
};
|
|
|
|
let mut first_card = Self::default();
|
|
let mut second_card = Self::default();
|
|
|
|
macro_rules! perform_cut {
|
|
( $dir:ident, $index:literal ) => {
|
|
if is_cut($index % 3, $index / 3) {
|
|
second_card.cells[$index] = self.cells[$index];
|
|
second_card.$dir = self.$dir;
|
|
} else {
|
|
first_card.cells[$index] = self.cells[$index];
|
|
first_card.$dir = self.$dir;
|
|
}
|
|
};
|
|
|
|
( $index:literal ) => {
|
|
if is_cut($index % 3, $index / 3) {
|
|
second_card.cells[$index] = self.cells[$index];
|
|
} else {
|
|
first_card.cells[$index] = self.cells[$index];
|
|
}
|
|
};
|
|
}
|
|
|
|
perform_cut!(nw, 0);
|
|
perform_cut!(n, 1);
|
|
perform_cut!(ne, 2);
|
|
perform_cut!(w, 3);
|
|
perform_cut!(4);
|
|
perform_cut!(e, 5);
|
|
perform_cut!(sw, 6);
|
|
perform_cut!(s, 7);
|
|
perform_cut!(se, 8);
|
|
|
|
(first_card, second_card)
|
|
}
|
|
|
|
pub fn flip(self, flip: FlipDir) -> Self {
|
|
let mut new_card = Self::default();
|
|
|
|
let transform = match flip {
|
|
FlipDir::Horizontal => |cell: Cell| match cell {
|
|
Cell::NW => Cell::NE,
|
|
Cell::NE => Cell::NW,
|
|
Cell::SW => Cell::SE,
|
|
Cell::SE => Cell::SW,
|
|
other => other,
|
|
},
|
|
FlipDir::Vertical => |cell: Cell| match cell {
|
|
Cell::NW => Cell::SW,
|
|
Cell::NE => Cell::SE,
|
|
Cell::SW => Cell::NW,
|
|
Cell::SE => Cell::NE,
|
|
other => other,
|
|
},
|
|
};
|
|
|
|
// Check for doors
|
|
macro_rules! assign_cell {
|
|
( ($source_dir:ident, $source_index:literal), ($dest_dir:ident, $dest_index:literal), $repeat:expr ) => {
|
|
new_card.cells[$dest_index] = transform(self.cells[$source_index]);
|
|
new_card.$dest_dir = self.$source_dir;
|
|
if $repeat == true {
|
|
new_card.cells[$source_index] = transform(self.cells[$dest_index]);
|
|
new_card.$source_dir = self.$dest_dir;
|
|
}
|
|
};
|
|
}
|
|
|
|
match flip {
|
|
FlipDir::Horizontal => {
|
|
assign_cell!((nw, 0), (ne, 2), true);
|
|
assign_cell!((w, 3), (e, 5), true);
|
|
assign_cell!((sw, 6), (se, 8), true);
|
|
assign_cell!((n, 1), (n, 1), false);
|
|
assign_cell!((s, 7), (s, 7), false);
|
|
}
|
|
FlipDir::Vertical => {
|
|
assign_cell!((nw, 0), (sw, 6), true);
|
|
assign_cell!((n, 1), (s, 7), true);
|
|
assign_cell!((ne, 2), (se, 8), true);
|
|
assign_cell!((w, 3), (w, 3), false);
|
|
assign_cell!((e, 5), (e, 5), false);
|
|
}
|
|
}
|
|
|
|
new_card.cells[4] = transform(self.cells[4]);
|
|
|
|
new_card
|
|
}
|
|
|
|
pub fn transpose(self, other: Self, selection: TransposeSelection) -> (Self, Self) {
|
|
let is_transposed: fn(usize, usize) -> bool = match selection {
|
|
TransposeSelection::Column(TransposeIndex::First) => |x, _| x == 0,
|
|
TransposeSelection::Column(TransposeIndex::Second) => |x, _| x == 1,
|
|
TransposeSelection::Column(TransposeIndex::Third) => |x, _| x == 2,
|
|
TransposeSelection::Row(TransposeIndex::First) => |_, y| y == 0,
|
|
TransposeSelection::Row(TransposeIndex::Second) => |_, y| y == 1,
|
|
TransposeSelection::Row(TransposeIndex::Third) => |_, y| y == 2,
|
|
};
|
|
|
|
let mut first_card = Self::default();
|
|
let mut second_card = Self::default();
|
|
|
|
macro_rules! perform_transpose {
|
|
( $dir:ident, $index:literal ) => {
|
|
if is_transposed($index % 3, $index / 3) {
|
|
first_card.cells[$index] = other.cells[$index];
|
|
second_card.cells[$index] = self.cells[$index];
|
|
|
|
first_card.$dir = other.$dir;
|
|
second_card.$dir = self.$dir;
|
|
} else {
|
|
first_card.cells[$index] = self.cells[$index];
|
|
second_card.cells[$index] = other.cells[$index];
|
|
|
|
first_card.$dir = self.$dir;
|
|
second_card.$dir = other.$dir;
|
|
}
|
|
};
|
|
|
|
( $index:literal ) => {
|
|
if is_transposed($index % 3, $index / 3) {
|
|
first_card.cells[$index] = other.cells[$index];
|
|
second_card.cells[$index] = self.cells[$index];
|
|
} else {
|
|
first_card.cells[$index] = self.cells[$index];
|
|
second_card.cells[$index] = other.cells[$index];
|
|
}
|
|
};
|
|
}
|
|
|
|
perform_transpose!(nw, 0);
|
|
perform_transpose!(n, 1);
|
|
perform_transpose!(ne, 2);
|
|
perform_transpose!(w, 3);
|
|
perform_transpose!(4);
|
|
perform_transpose!(e, 5);
|
|
perform_transpose!(sw, 6);
|
|
perform_transpose!(s, 7);
|
|
perform_transpose!(se, 8);
|
|
|
|
(first_card, second_card)
|
|
}
|
|
|
|
pub fn rotate(self, dir: RotationDir) -> Self {
|
|
let mut new_card = Self::default();
|
|
|
|
let transform = match dir {
|
|
RotationDir::Clockwise => |cell: Cell| match cell {
|
|
Cell::NW => Cell::NE,
|
|
Cell::NE => Cell::SE,
|
|
Cell::SE => Cell::SW,
|
|
Cell::SW => Cell::NW,
|
|
other => other,
|
|
},
|
|
RotationDir::CounterClockwise => |cell: Cell| match cell {
|
|
Cell::NW => Cell::SW,
|
|
Cell::SW => Cell::SE,
|
|
Cell::SE => Cell::NW,
|
|
Cell::NE => Cell::NW,
|
|
other => other,
|
|
},
|
|
};
|
|
|
|
// Check for doors
|
|
macro_rules! assign_cell {
|
|
( ($source_dir:ident, $source_index:literal), ($dest_dir:ident, $dest_index:literal) ) => {
|
|
new_card.cells[$dest_index] = transform(self.cells[$source_index]);
|
|
new_card.$dest_dir = self.$source_dir;
|
|
};
|
|
}
|
|
|
|
match dir {
|
|
RotationDir::Clockwise => {
|
|
assign_cell!((nw, 0), (ne, 2));
|
|
assign_cell!((n, 1), (e, 5));
|
|
assign_cell!((ne, 2), (se, 8));
|
|
assign_cell!((e, 5), (s, 7));
|
|
assign_cell!((se, 8), (sw, 6));
|
|
assign_cell!((s, 7), (w, 3));
|
|
assign_cell!((sw, 6), (nw, 0));
|
|
assign_cell!((w, 3), (n, 1));
|
|
}
|
|
RotationDir::CounterClockwise => {
|
|
assign_cell!((nw, 0), (sw, 6));
|
|
assign_cell!((w, 3), (s, 7));
|
|
assign_cell!((sw, 6), (se, 8));
|
|
assign_cell!((s, 7), (e, 5));
|
|
assign_cell!((se, 8), (ne, 2));
|
|
assign_cell!((e, 5), (n, 1));
|
|
assign_cell!((ne, 2), (nw, 0));
|
|
assign_cell!((n, 1), (w, 3));
|
|
}
|
|
}
|
|
|
|
new_card.cells[4] = transform(self.cells[4]);
|
|
|
|
new_card
|
|
}
|
|
}
|
|
|
|
#[rustfmt::skip]
|
|
pub const FULL_SQUARE: [Cell; 9] = [
|
|
Cell::Filled, Cell::Filled, Cell::Filled,
|
|
Cell::Filled, Cell::Filled, Cell::Filled,
|
|
Cell::Filled, Cell::Filled, Cell::Filled,
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
pub const NW_TRIANGLE: [Cell; 9] = [
|
|
Cell::Empty, Cell::Empty, Cell::NW,
|
|
Cell::Empty, Cell::NW, Cell::Filled,
|
|
Cell::NW, Cell::Filled, Cell::Filled,
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
pub const SW_TRIANGLE: [Cell; 9] = [
|
|
Cell::SW, Cell::Filled, Cell::Filled,
|
|
Cell::Empty, Cell::SW, Cell::Filled,
|
|
Cell::Empty, Cell::Empty, Cell::SW,
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
pub const NE_TRIANGLE: [Cell; 9] = [
|
|
Cell::NE, Cell::Empty, Cell::Empty,
|
|
Cell::Filled, Cell::NE, Cell::Empty,
|
|
Cell::Filled, Cell::Filled, Cell::NE
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
pub const OCTAGON: [Cell; 9] = [
|
|
Cell::NW, Cell::Filled, Cell::NE,
|
|
Cell::Filled, Cell::Filled, Cell::Filled,
|
|
Cell::SW, Cell::Filled, Cell::SE,
|
|
];
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn check_merge() {
|
|
let bottom = Card {
|
|
cells: NW_TRIANGLE,
|
|
..Default::default() // no doors
|
|
};
|
|
|
|
let top = Card {
|
|
cells: OCTAGON,
|
|
..Default::default()
|
|
};
|
|
|
|
// For now, triangular cells from the top-most Card survive the merge.
|
|
let expected = Card {
|
|
cells: [
|
|
Cell::NW,
|
|
Cell::Filled,
|
|
Cell::NE,
|
|
Cell::Filled,
|
|
Cell::Filled,
|
|
Cell::Filled,
|
|
Cell::SW,
|
|
Cell::Filled,
|
|
Cell::Filled,
|
|
],
|
|
..Default::default()
|
|
};
|
|
|
|
// Run the test
|
|
let (stacked, extra_doors) = bottom.merge(top);
|
|
assert_eq!(stacked, expected);
|
|
assert!(extra_doors.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn check_merge_doors() {
|
|
let bottom = Card {
|
|
cells: NW_TRIANGLE,
|
|
s: true, // Test that this is not overwritten by an empty tile
|
|
se: true, // Test that this is subsumed by another door
|
|
e: true, // Test that this is popped off into the second card
|
|
..Default::default()
|
|
};
|
|
|
|
let top = Card {
|
|
cells: [
|
|
Cell::Empty,
|
|
Cell::Empty,
|
|
Cell::Empty,
|
|
Cell::Empty,
|
|
Cell::Empty,
|
|
Cell::Filled,
|
|
Cell::Empty,
|
|
Cell::Empty,
|
|
Cell::Filled,
|
|
],
|
|
se: true, // Test that this is subsumed by another door
|
|
n: true, // Test that is is dropped because no floor
|
|
..Default::default()
|
|
};
|
|
|
|
let expected = Card {
|
|
cells: [
|
|
Cell::Empty,
|
|
Cell::Empty,
|
|
Cell::NW,
|
|
Cell::Empty,
|
|
Cell::NW,
|
|
Cell::Filled,
|
|
Cell::NW,
|
|
Cell::Filled,
|
|
Cell::Filled,
|
|
],
|
|
s: true,
|
|
se: true,
|
|
..Default::default()
|
|
};
|
|
|
|
let expected_doors = Card {
|
|
e: true,
|
|
..Default::default()
|
|
};
|
|
|
|
let (stacked, extra_doors) = bottom.merge(top);
|
|
assert_eq!(stacked, expected);
|
|
assert_eq!(extra_doors, Some(expected_doors));
|
|
}
|
|
|
|
/// Merging two triangular [`Cell`]s should prefer the top-most one, *unless*
|
|
/// they are opposites. See test [`merge_opposite_triangles()`].
|
|
#[test]
|
|
fn merge_triangle_and_triangle() {
|
|
let mut top = Card::default();
|
|
top.cells[0] = Cell::NW;
|
|
|
|
let mut bottom = Card::default();
|
|
bottom.cells[0] = Cell::NE;
|
|
|
|
let expected = top.clone();
|
|
|
|
let (stacked, doors) = bottom.merge(top);
|
|
assert_eq!(stacked, expected);
|
|
assert!(doors.is_none());
|
|
}
|
|
|
|
/// Merging a filled cell with anything should result in [`Cell::Filled`].
|
|
#[test]
|
|
fn merge_triangle_and_filled() {
|
|
let filled = Card {
|
|
cells: FULL_SQUARE,
|
|
..Default::default()
|
|
};
|
|
let triangle = Card {
|
|
cells: NW_TRIANGLE,
|
|
..Default::default()
|
|
};
|
|
|
|
let expected = filled.clone();
|
|
|
|
// Check the merge in both directions.
|
|
let result_fill_over_tri = triangle.merge(filled);
|
|
let result_tri_over_fill = filled.merge(triangle);
|
|
|
|
assert_eq!(expected, result_fill_over_tri.0);
|
|
assert_eq!(expected, result_tri_over_fill.0);
|
|
assert!(result_fill_over_tri.1.is_none());
|
|
assert!(result_tri_over_fill.1.is_none());
|
|
}
|
|
|
|
/// Merging a NW and SE cell should result in a single [`Cell::Filled`].
|
|
#[test]
|
|
fn merge_opposite_triangles() {
|
|
let mut top = Card::default();
|
|
top.cells[0] = Cell::NW;
|
|
|
|
let mut bottom = Card::default();
|
|
bottom.cells[0] = Cell::SE;
|
|
|
|
let mut expected = Card::default();
|
|
expected.cells[0] = Cell::Filled;
|
|
|
|
let (stacked, doors) = bottom.merge(top);
|
|
assert_eq!(stacked, expected);
|
|
assert!(doors.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn cut_octagon() {
|
|
// Original octagon
|
|
let octagon = Card {
|
|
cells: OCTAGON,
|
|
..Default::default()
|
|
};
|
|
|
|
// Pairs of each different slice option
|
|
let vert_left_pair = (
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::NW, Cell::Empty, Cell::Empty,
|
|
Cell::Filled, Cell::Empty, Cell::Empty,
|
|
Cell::SW, Cell::Empty, Cell::Empty,
|
|
],
|
|
..Default::default()
|
|
},
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Empty, Cell::Filled, Cell::NE,
|
|
Cell::Empty, Cell::Filled, Cell::Filled,
|
|
Cell::Empty, Cell::Filled, Cell::SE,
|
|
],
|
|
..Default::default()
|
|
},
|
|
);
|
|
|
|
let vert_right_pair = (
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::NW, Cell::Filled, Cell::Empty,
|
|
Cell::Filled, Cell::Filled, Cell::Empty,
|
|
Cell::SW, Cell::Filled, Cell::Empty,
|
|
],
|
|
..Default::default()
|
|
},
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Empty, Cell::Empty, Cell::NE,
|
|
Cell::Empty, Cell::Empty, Cell::Filled,
|
|
Cell::Empty, Cell::Empty, Cell::SE,
|
|
],
|
|
..Default::default()
|
|
},
|
|
);
|
|
|
|
let horiz_top_pair = (
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::NW, Cell::Filled, Cell::NE,
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
],
|
|
..Default::default()
|
|
},
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
Cell::Filled, Cell::Filled, Cell::Filled,
|
|
Cell::SW, Cell::Filled, Cell::SE,
|
|
],
|
|
..Default::default()
|
|
},
|
|
);
|
|
|
|
let horiz_bottom_pair = (
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::NW, Cell::Filled, Cell::NE,
|
|
Cell::Filled, Cell::Filled, Cell::Filled,
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
],
|
|
..Default::default()
|
|
},
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
Cell::SW, Cell::Filled, Cell::SE,
|
|
],
|
|
..Default::default()
|
|
},
|
|
);
|
|
|
|
// Run tests
|
|
let result_vleft = octagon.clone().cut(CutLine::VertLeft);
|
|
assert_eq!(result_vleft, vert_left_pair);
|
|
|
|
let result_vright = octagon.clone().cut(CutLine::VertRight);
|
|
assert_eq!(result_vright, vert_right_pair);
|
|
|
|
let result_hupper = octagon.clone().cut(CutLine::HorizUpper);
|
|
assert_eq!(result_hupper, horiz_top_pair);
|
|
|
|
let result_hlower = octagon.cut(CutLine::HorizLower);
|
|
assert_eq!(result_hlower, horiz_bottom_pair);
|
|
}
|
|
|
|
#[test]
|
|
fn cut_triangle() {
|
|
let tri = Card {
|
|
cells: NW_TRIANGLE,
|
|
..Default::default()
|
|
};
|
|
|
|
// Pairs of each different slice option
|
|
let vert_left_pair = (
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
Cell::NW, Cell::Empty, Cell::Empty,
|
|
],
|
|
..Default::default()
|
|
},
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Empty, Cell::Empty, Cell::NW,
|
|
Cell::Empty, Cell::NW, Cell::Filled,
|
|
Cell::Empty, Cell::Filled, Cell::Filled,
|
|
],
|
|
..Default::default()
|
|
},
|
|
);
|
|
|
|
let vert_right_pair = (
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
Cell::Empty, Cell::NW, Cell::Empty,
|
|
Cell::NW, Cell::Filled, Cell::Empty,
|
|
],
|
|
..Default::default()
|
|
},
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Empty, Cell::Empty, Cell::NW,
|
|
Cell::Empty, Cell::Empty, Cell::Filled,
|
|
Cell::Empty, Cell::Empty, Cell::Filled,
|
|
],
|
|
..Default::default()
|
|
},
|
|
);
|
|
|
|
let horiz_top_pair = (
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Empty, Cell::Empty, Cell::NW,
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
],
|
|
..Default::default()
|
|
},
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
Cell::Empty, Cell::NW, Cell::Filled,
|
|
Cell::NW, Cell::Filled, Cell::Filled,
|
|
],
|
|
..Default::default()
|
|
},
|
|
);
|
|
|
|
let horiz_bottom_pair = (
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Empty, Cell::Empty, Cell::NW,
|
|
Cell::Empty, Cell::NW, Cell::Filled,
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
],
|
|
..Default::default()
|
|
},
|
|
Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
Cell::Empty, Cell::Empty, Cell::Empty,
|
|
Cell::NW, Cell::Filled, Cell::Filled,
|
|
],
|
|
..Default::default()
|
|
},
|
|
);
|
|
|
|
// Run tests
|
|
let result_vleft = tri.clone().cut(CutLine::VertLeft);
|
|
assert_eq!(result_vleft, vert_left_pair);
|
|
|
|
let result_vright = tri.clone().cut(CutLine::VertRight);
|
|
assert_eq!(result_vright, vert_right_pair);
|
|
|
|
let result_hupper = tri.clone().cut(CutLine::HorizUpper);
|
|
assert_eq!(result_hupper, horiz_top_pair);
|
|
|
|
let result_hlower = tri.cut(CutLine::HorizLower);
|
|
assert_eq!(result_hlower, horiz_bottom_pair);
|
|
}
|
|
|
|
#[test]
|
|
fn flip_triangle_vertical() {
|
|
let tri = Card {
|
|
cells: NW_TRIANGLE,
|
|
..Default::default()
|
|
};
|
|
|
|
let expected = Card {
|
|
cells: SW_TRIANGLE,
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tri.flip(FlipDir::Vertical);
|
|
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn flip_triangle_horizontal() {
|
|
let tri = Card {
|
|
cells: NW_TRIANGLE,
|
|
..Default::default()
|
|
};
|
|
|
|
let expected = Card {
|
|
cells: NE_TRIANGLE,
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tri.flip(FlipDir::Horizontal);
|
|
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
/// Test transposition on column 1
|
|
#[test]
|
|
fn transpose_vertical() {
|
|
let left_shape = Card {
|
|
cells: OCTAGON,
|
|
..Default::default()
|
|
};
|
|
|
|
// It's a small NW triangle.
|
|
let right_shape = Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Filled, Cell::Empty, Cell::Empty,
|
|
Cell::SE, Cell::Empty, Cell::NW,
|
|
Cell::Empty, Cell::NW, Cell::Filled,
|
|
],
|
|
..Default::default()
|
|
};
|
|
|
|
let expected_left = Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Filled, Cell::Filled, Cell::NE,
|
|
Cell::SE, Cell::Filled, Cell::Filled,
|
|
Cell::Empty, Cell::Filled, Cell::SE,
|
|
],
|
|
..Default::default()
|
|
};
|
|
|
|
let expected_right = Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::NW, Cell::Empty, Cell::Empty,
|
|
Cell::Filled, Cell::Empty, Cell::NW,
|
|
Cell::SW, Cell::NW, Cell::Filled,
|
|
],
|
|
..Default::default()
|
|
};
|
|
|
|
let (res_left, res_right) = left_shape.transpose(
|
|
right_shape,
|
|
TransposeSelection::Column(TransposeIndex::First),
|
|
);
|
|
assert_eq!(res_left, expected_left);
|
|
assert_eq!(res_right, expected_right);
|
|
}
|
|
|
|
/// Test transposition on row 3
|
|
#[test]
|
|
fn transpose_horizontal() {
|
|
let left_shape = Card {
|
|
cells: OCTAGON,
|
|
..Default::default()
|
|
};
|
|
|
|
let right_shape = Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Filled, Cell::Empty, Cell::Empty,
|
|
Cell::SE, Cell::Empty, Cell::NW,
|
|
Cell::Empty, Cell::NW, Cell::Filled,
|
|
],
|
|
..Default::default()
|
|
};
|
|
|
|
let expected_left = Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::NW, Cell::Filled, Cell::NE,
|
|
Cell::Filled, Cell::Filled, Cell::Filled,
|
|
Cell::Empty, Cell::NW, Cell::Filled,
|
|
],
|
|
..Default::default()
|
|
};
|
|
|
|
let expected_right = Card {
|
|
#[rustfmt::skip]
|
|
cells: [
|
|
Cell::Filled, Cell::Empty, Cell::Empty,
|
|
Cell::SE, Cell::Empty, Cell::NW,
|
|
Cell::SW, Cell::Filled, Cell::SE,
|
|
],
|
|
..Default::default()
|
|
};
|
|
|
|
let (res_left, res_right) =
|
|
left_shape.transpose(right_shape, TransposeSelection::Row(TransposeIndex::Third));
|
|
assert_eq!(res_left, expected_left);
|
|
assert_eq!(res_right, expected_right);
|
|
}
|
|
|
|
#[test]
|
|
fn rotate_clockwise() {
|
|
let shape = Card {
|
|
cells: NW_TRIANGLE,
|
|
..Default::default()
|
|
};
|
|
|
|
let expected = Card {
|
|
cells: NE_TRIANGLE,
|
|
..Default::default()
|
|
};
|
|
|
|
let result = shape.rotate(RotationDir::Clockwise);
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn rotate_counter_clockwise() {
|
|
let shape = Card {
|
|
cells: NE_TRIANGLE,
|
|
..Default::default()
|
|
};
|
|
|
|
let expected = Card {
|
|
cells: NW_TRIANGLE,
|
|
..Default::default()
|
|
};
|
|
|
|
let result = shape.rotate(RotationDir::CounterClockwise);
|
|
assert_eq!(result, expected);
|
|
}
|
|
}
|