use crate::puzzle::{label::label::Label, sliding_puzzle::SlidingPuzzle};
use itertools::Itertools as _;
pub trait SolvedState {
#[must_use]
fn is_solved<Puzzle>(&self, puzzle: &Puzzle) -> bool
where
Puzzle: SlidingPuzzle,
Self: Sized;
}
impl<L: Label> SolvedState for L {
fn is_solved<Puzzle>(&self, puzzle: &Puzzle) -> bool
where
Puzzle: SlidingPuzzle,
{
let size = puzzle.size();
let (w, h) = size.into();
if puzzle.gap_position_xy() != (w - 1, h - 1) {
return false;
}
(0..w)
.cartesian_product(0..h)
.take(size.num_pieces() as usize)
.all(|(x, y)| {
let solved_pos = puzzle.solved_pos_xy(puzzle.piece_at_xy((x, y)));
let piece_label = self.try_position_label(size, solved_pos);
let solved_label = self.try_position_label(size, (x, y));
piece_label == solved_label
})
}
}
#[cfg(test)]
mod tests {
use crate::puzzle::puzzle::Puzzle;
use std::str::FromStr as _;
macro_rules! test_solved_state {
(fn $name:ident, $i:literal, $label:expr, $ok:literal : $pos:literal) => {
#[test]
fn $name() {
let p = Puzzle::from_str($pos).unwrap();
if $ok {
assert!($label.is_solved(&p));
} else {
assert!(!$label.is_solved(&p));
}
}
};
($label:ty, $($i:literal : $ok:literal : $pos:literal),+ $(,)?) => {
::paste::paste! {
mod [< $label:snake >] {
use crate::puzzle::{label::label::$label, solved_state::SolvedState as _};
use super::*;
$(test_solved_state!(
fn [< test_ $label:snake _ $i >] , $i, $label, $ok : $pos);
)*
}
}
};
}
test_solved_state!(
Trivial,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: false: "1 2 3 4/5 6 7 8/9 0 11 12/13 14 15 10",
3: true: "1 2/3 4/5 6/7 8/9 10/11 12/13 14/15 16/17 18/19 0",
4: false: "10 8/15 3/11 18/5 2/6 19/13 9/12 14/0 4/16 7/1 17",
5: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
);
test_solved_state!(
RowGrids,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: false: "1 2 3 4/5 6 7 8/9 0 11 12/13 14 15 10",
3: true: "1 2/3 4/5 6/7 8/9 10/11 12/13 14/15 16/17 18/19 0",
4: false: "1 2/3 4/7 8/5 6/9 10/11 12/13 14/15 16/17 18/19 0",
5: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
);
test_solved_state!(
Rows,
1: true: "4 1 3 2/6 7 8 5/9 10 11 12/14 13 15 0",
2: false: "1 2 3 5/4 6 7 8/9 10 11 12/13 14 15 0",
3: false: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 0 15",
4: true: "1 2 3/6 5 4/9 7 8/12 11 10/14 15 13/16 17 0",
5: false: "1 2 6/3 5 4/9 7 8/12 11 10/14 15 13/16 17 0",
6: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
);
test_solved_state!(
Fringe,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: true: "13 4 5 3/1 8 14 7/2 10 15 11/9 6 12 0",
3: false: "13 8 5 3/1 4 14 7/2 10 15 11/9 6 12 0",
4: false: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 0 15",
5: true: "9 8 7 1 2 3 6 5 11 4/10 13 12 15 14 17 16 19 18 0",
6: false: "9 8 7 1 2 3 6 5 12 4/10 13 11 15 14 17 16 19 18 0",
7: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
8: true: "4 21 17 13/9 8 22 18/5 14 19 23/2 10 11 20/1 7 15 16/3 6 12 0",
);
test_solved_state!(
FringeGrids,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: false: "1 2 3 4/5 6 7 8/9 0 11 12/13 14 15 10",
3: true: "1 2/3 4/5 6/7 8/9 10/11 12/13 14/15 16/17 18/19 0",
4: false: "1 2/3 4/7 8/5 6/9 10/11 12/13 14/15 16/17 18/19 0",
5: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
);
test_solved_state!(
SquareFringe,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: true: "13 4 5 3/1 8 14 7/2 10 15 11/9 6 12 0",
3: false: "13 8 5 3/1 4 14 7/2 10 15 11/9 6 12 0",
4: false: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 0 15",
5: true: "11 2 13 4 15 6 17 8 19 9/1 12 3 14 5 16 7 18 10 0",
6: false: "2 1 3 4 5 6 7 8 9 10/11 12 13 14 15 16 17 18 19 0",
7: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
8: true: "4 1 3 2/8 7 6 5/9 13 17 21/10 18 14 22/12 16 23 19/11 15 20 0",
);
test_solved_state!(
SplitFringe,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: true: "4 1 3 2/13 7 6 8/5 14 12 11/9 10 15 0",
3: false: "13 4 5 3/1 8 14 7/2 10 15 11/9 6 12 0",
4: false: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 0 15",
5: true: "10 9 8 7 6 5 4 3 2 1/11 19 18 17 16 15 14 13 12 0",
6: false: "10 9 8 7 6 5 4 3 2 1/12 19 18 17 16 15 14 13 11 0",
7: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
8: true: "4 3 2 1/17 8 6 7/21 14 12 11/13 10 23 16/9 22 15 20/5 18 19 0",
9: false: "4 3 2 1/17 8 6 7/21 14 12 11/13 10 23 20/9 22 15 16/5 18 19 0",
);
test_solved_state!(
SplitSquareFringe,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: false: "4 1 3 2/13 7 6 8/5 14 15 11/9 10 12 0",
3: false: "13 4 5 3/1 8 14 7/2 10 15 11/9 6 12 0",
4: false: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 0 15",
5: true: "11 2 13 4 15 6 17 8 10 9/1 12 3 14 5 16 7 18 19 0",
6: false: "11 2 13 4 15 6 17 8 19 9/1 12 3 14 5 16 7 18 10 0",
7: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
8: true: "4 1 3 2/8 7 6 5/9 12 11 10/17 16 14 15/13 22 20 19/21 18 23 0",
);
test_solved_state!(
Diagonals,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: true: "1 5 6 10/2 9 4 14/3 13 8 15/7 11 12 0",
3: false: "5 1 6 10/2 9 4 14/3 13 8 15/7 11 12 0",
4: false: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 0 15",
5: true: "1 11 12 13 14 15 16 17 18 19/2 3 4 5 6 7 8 9 10 0",
6: false: "1 11 12 13 14 15 16 17 18 19/3 2 4 5 6 7 8 9 10 0",
7: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
8: true: "1 5 6 10/2 9 4 14/3 13 8 15/7 17 18 19/11 21 16 23/12 22 20 0",
);
test_solved_state!(
LastTwoRows,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: true: "4 3 2 1/6 5 8 7/13 14 15 12/9 10 11 0",
3: false: "5 1 6 10/2 9 4 14/3 13 8 15/7 11 12 0",
4: false: "1 2 3 4/5 6 7 8/9 10 11 0/13 14 15 12",
5: true: "11 2 13 4 15 6 17 8 19 10/1 12 3 14 5 16 7 18 9 0",
6: false: "2 1 3 4 5 6 7 8 9 10/11 12 13 14 15 16 17 18 19 0",
7: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
8: true: "2 1 4 3/7 8 5 6/10 11 12 9/16 13 14 15/17 22 19 20/21 18 23 0",
);
test_solved_state!(
SplitLastTwoRows,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: true: "9 13 3 4/5 10 14 8/2 7 11 12/1 6 15 0",
3: false: "5 1 6 10/2 9 4 14/3 13 8 15/7 11 12 0",
4: false: "1 2 3 4/5 6 7 8/9 10 11 0/13 14 15 12",
5: true: "11 2 13 4 15 6 17 8 19 10/1 12 3 14 5 16 7 18 9 0",
6: false: "2 1 3 4 5 6 7 8 9 10/11 12 13 14 15 16 17 18 19 0",
7: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
8: true: "2 1 4 21/22 8 5 6/10 11 12 9/16 13 14 15/17 7 19 20/3 18 23 0",
);
test_solved_state!(
ConcentricRectangles,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: true: "2 5 1 13/12 11 6 15/4 10 7 8/9 14 3 0",
3: false: "13 4 2 1/9 12 3 7/5 15 11 6/14 8 10 0",
4: false: "1 2 3 4/5 6 7 8/9 10 11 0/13 14 15 12",
5: true: "2 10 15 1 4 6 19 5 16 13/8 18 14 17 7 9 11 12 3 0",
6: false: "1 2 3 4 5 6 7 8 9 10/11 12 13 14 15 16 17 18 0 19",
7: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
8: true: "9 3 22 23/20 6 11 13/1 7 10 4/21 19 18 17/5 14 15 8/16 2 12 0",
);
test_solved_state!(
Spiral,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: true: "3 1 2 8/13 6 7 4/5 10 11 12/9 15 14 0",
3: false: "14 10 13 3/5 1 7 15/4 9 8 12/11 6 2 0",
4: false: "1 2 3 4/5 6 7 8/9 10 11 0/13 14 15 12",
5: true: "9 8 7 6 5 4 3 2 1 10/11 19 18 17 16 15 14 13 12 0",
6: false: "9 8 7 6 5 4 3 2 1 10/19 11 18 17 16 15 14 13 12 0",
7: false: "9 8 7 6 5 4 3 2 10 1/11 19 18 17 16 15 14 13 12 0",
8: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
9: true: "3 1 2 12/21 6 15 4/5 10 11 16/17 18 7 20/9 14 19 8/13 22 23 0",
);
test_solved_state!(
SpiralGrids,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: false: "1 2 3 4/5 6 7 8/9 0 11 12/13 14 15 10",
3: true: "1 2/3 4/5 6/7 8/9 10/11 12/13 14/15 16/17 18/19 0",
4: false: "1 2/3 4/7 8/5 6/9 10/11 12/13 14/15 16/17 18/19 0",
5: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
);
test_solved_state!(
Checkerboard,
1: true: "1 2 3 4/5 6 7 8/9 10 11 12/13 14 15 0",
2: true: "11 4 6 12/2 14 15 1/9 5 3 13/7 8 10 0",
3: false: "11 4 6 12/2 0 5 1/9 15 14 10/7 8 13 3",
4: true: "1 15 12 10 3 19 5 2 14 11/4 9 6 7 8 16 13 18 17 0",
5: false: "3 17 5 1 6 4 18 7 2 13/8 12 11 15 10 14 9 16 19 0",
6: false: "3 8 9 10/4 15 0 12/6 2 1 5/11 7 14 13",
);
}