diff --git a/src/engine/board.rs b/src/engine/board.rs index a2e5327..4803bfb 100644 --- a/src/engine/board.rs +++ b/src/engine/board.rs @@ -5,7 +5,11 @@ use std::{ }; use super::{ - cmove::CMove, constants::BOARD_SIZE, piece::Piece, square::{Square, SquarePair} + cmove::CMove, + constants::BOARD_SIZE, + errors::SError, + piece::Piece, + square::{Square, SquarePair}, }; #[derive(Clone)] @@ -34,6 +38,51 @@ impl Board { } } + pub(crate) fn from_id(board_id: u128) -> Result { + let mut board = Board::new(); + let mut working = board_id; + for i in (0..BOARD_SIZE).rev() { + for j in (0..BOARD_SIZE).rev() { + let mask = 0b111; + let piece = Board::get_piece_from_encoding((working & mask) as u8); + working = working >> 3; + let piece = piece?; + board.set(Square::new(i, j, piece)); + } + } + Ok(board) + } + + pub(crate) fn from_string(board_string: String) -> Result { + if board_string.chars().count() != 16 { + return Err(SError::InvalidBoard); + } + + let mut board = Board::new(); + let mut file = 0; + let mut rank = 0; + let mut chars = board_string.chars(); + for r in 0..BOARD_SIZE { + for f in 0..BOARD_SIZE { + let c = chars.next().unwrap(); + let piece = match c { + 'K' => Piece::King, + 'Q' => Piece::Queen, + 'B' => Piece::Bishop, + 'N' => Piece::Knight, + 'R' => Piece::Rook, + 'P' => Piece::Pawn, + '.' => continue, + _ => return Err(SError::InvalidBoard), + }; + + let square = Square::new(f, r, Some(piece)); + board.set(square); + } + } + Ok(board) + } + pub(crate) fn set(&mut self, square: Square) -> Option { let new_is_occuppied = square.piece.is_some(); let existing = mem::replace(&mut self.cells[square.file][square.rank], square.piece); @@ -84,6 +133,21 @@ impl Board { pub(crate) fn pretty_print(&self) { println!("{}", self.print(true)); + println!("{:^40}\n", format!("id: {}", self.id())); + } + + pub(crate) fn id(&self) -> u128 { + let mut res: u128 = 0; + + for i in 0..BOARD_SIZE { + for j in 0..BOARD_SIZE { + res = res << 3; + let byte = Board::get_piece_encoding(self.cells[i][j]); + res = res | byte as u128 + } + } + + res } fn print(&self, pretty: bool) -> String { @@ -239,40 +303,37 @@ impl Board { ret } - pub(crate) fn from_string(board_string: String) -> Option { - if board_string.chars().count() != 16 { - return None; - } - - let mut board = Board::new(); - let mut file = 0; - let mut rank = 0; - let mut chars = board_string.chars(); - for r in 0..BOARD_SIZE { - for f in 0..BOARD_SIZE { - let c = chars.next().unwrap(); - let piece = match c { - 'K' => Piece::King, - 'Q' => Piece::Queen, - 'B' => Piece::Bishop, - 'N' => Piece::Knight, - 'R' => Piece::Rook, - 'P' => Piece::Pawn, - '.' => continue, - _ => return None, - }; - - let square = Square::new(f, r, Some(piece)); - board.set(square); - } - } - Some(board) - } - fn board_state_changed(&mut self) { self.calc_legal_moves(); self.calc_game_state(); } + + fn get_piece_encoding(piece: Option) -> u8 { + match piece { + Some(p) => match p { + Piece::King => 0b001, + Piece::Queen => 0b010, + Piece::Rook => 0b011, + Piece::Bishop => 0b100, + Piece::Knight => 0b101, + Piece::Pawn => 0b110, + }, + None => 0b000, + } + } + + fn get_piece_from_encoding(encoding: u8) -> Result, SError> { + match encoding { + 0b001 => Ok(Some(Piece::King)), + 0b010 => Ok(Some(Piece::Queen)), + 0b011 => Ok(Some(Piece::Rook)), + 0b100 => Ok(Some(Piece::Bishop)), + 0b101 => Ok(Some(Piece::Knight)), + 0b110 => Ok(Some(Piece::Pawn)), + 0b000 => Ok(None), + _ => Err(SError::InvalidBoard), + } + } } fn get_square_for_display(piece: &Option, pretty: bool) -> String { @@ -490,4 +551,21 @@ mod tests { assert_eq!(1, board.pieces_remaining); assert_eq!(GameState::Won, board.game_state); } + + #[test] + fn test_encoding() { + let mut board = Board::new(); + board.set(sq!("Pa1")); + board.set(sq!("Ra2")); + board.set(sq!("Qb2")); + board.set(sq!("Kd2")); + board.set(sq!("Bd4")); + board.set(sq!("Nc4")); + + let id = board.id(); + let board2 = Board::from_id(id); + let board2 = board2.unwrap(); + + validate_board!(board2, "..NB", "....", "RQ.K", "P..."); + } } diff --git a/src/engine/errors.rs b/src/engine/errors.rs new file mode 100644 index 0000000..757a19c --- /dev/null +++ b/src/engine/errors.rs @@ -0,0 +1,4 @@ +#[derive(Debug)] +pub(crate) enum SError { + InvalidBoard, +} diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 4f15608..8fe34b3 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod constants; +pub(crate) mod errors; pub(crate) mod board; pub(crate) mod square; pub(crate) mod cmove; diff --git a/src/generator/generator.rs b/src/generator/generator.rs index f4fae1d..83ab99d 100644 --- a/src/generator/generator.rs +++ b/src/generator/generator.rs @@ -13,12 +13,11 @@ pub(crate) fn generate(num_pieces: u32, num_solutions: u32) -> GenerateStats { Piece::Pawn, Piece::Pawn, Piece::Pawn, - Piece::Rook, + Piece::Pawn, + Piece::Bishop, Piece::Bishop, Piece::Knight, Piece::Knight, - Piece::King, - Piece::Queen, ]; if num_pieces > candidate_pieces.len().try_into().unwrap() { diff --git a/src/main.rs b/src/main.rs index 385a182..90002aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,16 +25,21 @@ fn main() { if args.print { solve_puzzle(board); } - } else if let Some(board_string) = args.solve { - let board = Board::from_string(board_string); - let Some(board) = board else { - println!("Invalid board string"); + } else { + let board = if let Some(board_string) = args.solve_board { + Board::from_string(board_string) + } else if let Some(board_id) = args.solve { + Board::from_id(board_id) + } else { + println!("Use --help to see available options"); + return; + }; + let Ok(board) = board else { + println!("Invalid board string/id"); return; }; board.pretty_print(); solve_puzzle(board); - } else { - println!("Use --help to see available options"); } } @@ -100,6 +105,10 @@ struct Args { print: bool, #[argh(option, short = 's')] - /// the board to solve - solve: Option, + /// the id of the board to solve + solve: Option, + + #[argh(option)] + /// the board to solve in board representation + solve_board: Option, }