sokoban-elements 0.2.1

Parsing and writing Sokoban, Hexoban, Trioban and Octoban file formats.
Documentation
//! [`Trioban`](crate::Tessellation::Trioban) board text: parsing, printing and resizing.
//!
//! A stub by design, and it should stay one. Every method body comes from the `BoardParser` /
//! `BoardPrinter` / `BoardResizer` defaults in `parser.rs`; all this module declares is which
//! tessellation's padding rule those defaults apply. Shared behavior therefore belongs in a trait
//! default rather than here.

use super::parser::{BoardParser, BoardPrinter, BoardResizer};
use crate::Tessellation;

pub struct TriobanBoardParser {}

impl BoardParser for TriobanBoardParser {}

impl BoardPrinter for TriobanBoardParser {}

impl BoardResizer for TriobanBoardParser {
    const TESSELLATION: Tessellation = Tessellation::Trioban;
}

#[cfg(test)]
mod tests {
    use super::super::*;

    /// A clockwise turn, cell by cell.
    ///
    /// One cell at a time is marked and located after the turn, the idiom of part F of
    /// `doc/dev-notes/geometry_probe.rs`, rather than asserting `printed()`: a 60 degree turn flips
    /// triangle orientation, so a rotated board's *text* legitimately changes shape and reads as
    /// nothing in particular.
    ///
    /// Expected positions are computed from the section 4 closed form plus the smallest-area
    /// anchor, which for 4x2 is `dx = 4, dy = 0`, giving a 5x3 result. Two rows of four columns is
    /// the least that shows both triangle orientations and both column parities.
    mod test_rotation {
        use super::*;

        #[rustfmt::skip]
        #[test]
        fn it_rotates_every_cell_to_its_computed_position() -> Result<(), BoardSyntaxError> {
            const WIDTH: usize = 4;
            const HEIGHT: usize = 2;

            let expectations: [((usize, usize), (usize, usize)); 8] = [
                ((0, 0), (3, 0)), ((1, 0), (3, 1)), ((2, 0), (4, 1)), ((3, 0), (4, 2)),
                ((0, 1), (1, 1)), ((1, 1), (2, 1)), ((2, 1), (2, 2)), ((3, 1), (3, 2)),
            ];

            for ((x, y), wanted) in expectations {
                let mut tiles = [TileKind::Floor; WIDTH * HEIGHT];
                tiles[y * WIDTH + x] = TileKind::Wall;

                let mut board =
                    GameBoard::parsed_from_tiles(&tiles, WIDTH, HEIGHT, Tessellation::Trioban)?;
                board.rotate(Rotation::Clockwise);

                assert_eq!((board.width(), board.height()), (5, 3));
                assert_eq!(
                    board.iter().find(|(_, tile)| *tile == TileKind::Wall).map(|(at, _)| at),
                    Some(wanted),
                    "the cell at ({x}, {y})"
                );
            }

            Ok(())
        }
    }
}