From d6119de05ad6894466ebbc4ed9ae083e76055626 Mon Sep 17 00:00:00 2001 From: cool-mist Date: Sat, 4 Jan 2025 19:29:22 +0530 Subject: [PATCH] Add solver --- src/engine/board.rs | 11 +++-- src/main.rs | 3 ++ src/solver/mod.rs | 1 + src/solver/solver.rs | 110 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 src/solver/mod.rs create mode 100644 src/solver/solver.rs diff --git a/src/engine/board.rs b/src/engine/board.rs index c378d4a..235b064 100644 --- a/src/engine/board.rs +++ b/src/engine/board.rs @@ -7,14 +7,15 @@ use super::{ square::{sq, Square}, }; +#[derive(Clone)] pub(crate) struct Board { pub(crate) cells: [[Square; 4]; 4], - legal_moves: HashSet, - pieces_remaining: i8, - game_state: GameState, + pub(crate) legal_moves: HashSet, + pub(crate) game_state: GameState, + pieces_remaining: u8, } -#[derive(PartialEq, Eq, Debug)] +#[derive(PartialEq, Eq, Debug, Clone)] pub enum GameState { NotStarted, InProgress, @@ -256,7 +257,7 @@ impl Board { if (start.rank < 3 && start.file > 0) { let mut south = start.rank; let mut west = start.file; - while south < 4 && west != 0 { + while south < 3 && west != 0 { south += 1; west -= 1; if let Some(piece) = self.cells[west][south].occupied() { diff --git a/src/main.rs b/src/main.rs index 1aab287..cc451e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,7 @@ #[allow(unused)] mod engine; +#[allow(unused)] +mod solver; + fn main() {} diff --git a/src/solver/mod.rs b/src/solver/mod.rs new file mode 100644 index 0000000..7f10d6f --- /dev/null +++ b/src/solver/mod.rs @@ -0,0 +1 @@ +pub(crate) mod solver; diff --git a/src/solver/solver.rs b/src/solver/solver.rs new file mode 100644 index 0000000..362bff6 --- /dev/null +++ b/src/solver/solver.rs @@ -0,0 +1,110 @@ +use crate::engine::{ + board::{Board, GameState}, + r#move::Move, +}; + +pub(crate) struct Solver { + pub(crate) board: Board, + moves: Vec, +} + +impl Solver { + pub(crate) fn new(board: Board) -> Solver { + Solver { + board, + moves: vec![], + } + } + + fn clone(&self, m: Move) -> Self { + let mut moves = self.moves.clone(); + let mut board = self.board.clone(); + moves.push(m.clone()); + board.make_move(m); + Solver { board, moves } + } + + pub(crate) fn solve(&self) -> Vec> { + let mut solutions = Vec::new(); + if let GameState::Won = self.board.game_state { + solutions.push(self.moves.clone()); + return solutions; + } + + let GameState::InProgress = self.board.game_state else { + return solutions; + }; + + self.board.legal_moves.iter().for_each(|m| { + let mut solver = self.clone(m.clone()); + let more_solutions = solver.solve(); + solutions.extend(more_solutions); + }); + + solutions + } +} + +#[cfg(test)] +mod tests { + use crate::engine::{ + coord::{at, Coord}, + piece::{p, Piece}, + square::{sq, Square}, + }; + + use super::*; + + #[test] + fn solver_smoke() { + let mut board = Board::new(); + // . R . . + // R . . P + // B . B N + // P . N . + + board.set(sq!("P", "a1")); + board.set(sq!("B", "a2")); + board.set(sq!("R", "a3")); + board.set(sq!("R", "b4")); + board.set(sq!("N", "c1")); + board.set(sq!("B", "c2")); + board.set(sq!("N", "d2")); + board.set(sq!("P", "d3")); + + let solver = Solver::new(board.clone()); + let solutions = solver.solve(); + + assert_eq!(10, solutions.len()); + + for solution in solutions { + let mut board = board.clone(); + solution.iter().for_each(|m| { + board.make_move(m.clone()); + }); + assert_eq!(GameState::Won, board.game_state); + } + } + + #[test] + fn solver_smoke_no_solution() { + // . R . . + // R . . . + // B . B N + // P . N . + + let mut board = Board::new(); + board.set(sq!("P", "a1")); + board.set(sq!("B", "a2")); + board.set(sq!("R", "a3")); + board.set(sq!("R", "b4")); + board.set(sq!("N", "c1")); + board.set(sq!("B", "c2")); + board.set(sq!("N", "d2")); + + let solver = Solver::new(board.clone()); + let solutions = solver.solve(); + + assert_eq!(0, solutions.len()); + } +}