diff --git a/.idea/bamboo.iml b/.idea/bamboo.iml index c254557..394aa25 100644 --- a/.idea/bamboo.iml +++ b/.idea/bamboo.iml @@ -1,5 +1,10 @@ + + + + + @@ -7,5 +12,6 @@ + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c2fea0b..4a7ce5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,9 +12,16 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" name = "bamboo" version = "0.1.0" dependencies = [ + "hex", "spin", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "lock_api" version = "0.4.9" diff --git a/Cargo.toml b/Cargo.toml index 01f81eb..97cd20b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -spin = "0.9.5" \ No newline at end of file +spin = "0.9.5" +hex = "0.4.3" \ No newline at end of file diff --git a/positions.csv b/positions.csv new file mode 100644 index 0000000..63090d4 --- /dev/null +++ b/positions.csv @@ -0,0 +1 @@ +1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,1,1,0,1,1,0,1,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,1,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0,1,0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,-,0,-2801 \ No newline at end of file diff --git a/src/board.rs b/src/board.rs index 1b2a2ae..08cf0d0 100644 --- a/src/board.rs +++ b/src/board.rs @@ -5,7 +5,7 @@ use crate::boardfield::{Boardfield, BoardfieldOps}; use crate::piece::{PieceColor, PieceOnBoard, PieceType}; use crate::utils::{algebraic_to_boardloc, AlgebraicNotationError, boardloc_to_algebraic}; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct Board { bitfield: Boardfield, // While this is a less efficient memory layout, having an array of where all the pieces are will make move generation significantly faster. @@ -48,6 +48,66 @@ impl Display for FENParseError { } impl Board { + pub fn to_board_id(&self) -> [u8; 41] { + // 0-31 BOARDFILED + // 32 FLAGS + // 33-36 EN_PASSANT_TARGET + // 37-38 HALFMOVE_COUNTER + // 39-40 FULLMOVE_COUNTER + + let mut res = [0u8; 41]; + + res[0..32].copy_from_slice(&self.bitfield.get_field()); + + let mut flags_byte = res[32]; + // FLAGS BYTE: + // + // / unused + // | + // 000 0 0 0 0 0 + // | | | | \ + // | | | | turn to move (1 white, 0 black) + // | | | \ + // | | | white kingside castling + // | | \ + // | | white queenside castling + // | \ + // | black kingside castling + // \ + // black queenside castling + if self.turn == PieceColor::White { + flags_byte |= 0b1; + } + if self.castle_white_kingside { + flags_byte |= 0b1 << 1; + } + if self.castle_white_queenside { + flags_byte |= 0b1 << 2; + } + if self.castle_black_kingside { + flags_byte |= 0b1 << 3; + } + if self.castle_black_queenside { + flags_byte |= 0b1 << 4; + } + res[32] = flags_byte; + + let en_passant_target: [u8; 4] = match self.en_passant_target { + Some(target) => { + (target as u32).to_le_bytes() + }, + None => { + u32::MAX.to_le_bytes() + } + }; + res[33..37].copy_from_slice(&en_passant_target); + + res[37..39].copy_from_slice(&(self.halfmove_counter as u16).to_le_bytes()); + res[39..41].copy_from_slice(&(self.move_counter as u16).to_le_bytes()); + + res + } + pub fn from_fen(fen: &str) -> Result { let components = fen.split(' ').collect::>(); if components.len() != 6 { @@ -115,7 +175,7 @@ impl Board { file += num; }, '/' => { - if rank == 8 { + if rank == 7 { return Err(FENParseError::CannotSkipToOutsideOfBoard { got: 1, which_is: file + 1 }) } @@ -142,7 +202,6 @@ impl Board { } fn create_fen_piece(c: char, rank: usize, file: usize) -> Result { - println!("playing {} at {} ({}, {})", c, rank + file * 8, file, rank); Ok(PieceOnBoard { loc: rank + file * 8, value: match c { @@ -189,6 +248,41 @@ mod tests { use crate::boardloc; use crate::piece::{PieceColor, PieceType}; + #[test] + pub fn test_serialization() { + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 1, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w q - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 17, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w k - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 9, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w kq - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 25, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w Q - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 5, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w Qq - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 21, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w Qk - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 13, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w Qkq - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 29, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w K - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 3, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w Kq - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 19, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w Kk - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 11, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQ - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 7, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQq - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 23, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQk - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 15, 255, 255, 255, 255, 0, 0, 1, 0]); + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 31, 255, 255, 255, 255, 0, 0, 1, 0]); + + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq a4 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 30, 3, 0, 0, 0, 0, 0, 1, 0]); + + assert!(Board::from_fen("d").is_err()); + assert!(Board::from_fen("8/8/8/8/8/8/8/8 h - - 0 1").is_err()); + assert!(Board::from_fen("8/8/8/8/8/8/8 w KQkq j4 0 1").is_err()); + + assert!(Board::from_fen("9/8/8/8/8/8/8 w KQkq a4 0 1").is_err()); + assert!(Board::from_fen("18/8/8/8/8/8/8 w KQkq a4 0 1").is_err()); + assert!(Board::from_fen("8/8/8/8/8/8/8/8/8 w KQkq a4 0 1").is_err()); + assert!(Board::from_fen("8/8/8/8/8/8/8/8 w KQkq a4 a 1").is_err()); + assert!(Board::from_fen("8/8/8/8/8/8/8/8 w KQkq a4 0 a").is_err()); + + assert!(Board::from_fen("s w KQkq a4 0 1").is_err()); + + assert_eq!(Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap().to_board_id(), [156, 0, 0, 65, 154, 0, 0, 33, 155, 0, 0, 49, 157, 0, 0, 81, 158, 0, 0, 97, 155, 0, 0, 49, 154, 0, 0, 33, 156, 0, 0, 65, 31, 255, 255, 255, 255, 0, 0, 1, 0]); + } + #[test] pub fn fen_parse_testing() { let board = Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap(); diff --git a/src/boardfield.rs b/src/boardfield.rs index 0e54935..3fd601f 100644 --- a/src/boardfield.rs +++ b/src/boardfield.rs @@ -33,6 +33,7 @@ pub trait BoardfieldOps { fn startpos() -> Self where Self: Sized; fn get_pos(&self, boardloc: usize) -> u8; fn set_pos(&mut self, boardloc: usize, piece: u8); + fn get_field(&self) -> [u8; 32]; } impl BoardfieldOps for Boardfield { fn new() -> Self where Self: Sized { @@ -117,6 +118,12 @@ impl BoardfieldOps for Boardfield { self[boardloc / 2] = bf_new; } + + fn get_field(&self) -> [u8; 32] { + *self + } + + } #[cfg(test)] diff --git a/src/main.rs b/src/main.rs index 4ba4aa8..b6f9293 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use std::path::Path; use std::sync::{Arc}; use crate::uci::{uci_handle_command, UCIState}; use spin::Mutex; +use crate::board::Board; #[cfg(not(tarpaulin_include))] // UCI parse engine is not tested with tarpaulin, outer test harness is planned pub mod uci; @@ -19,6 +20,29 @@ pub const ENGINE_AUTHOR: &str = "c0repwn3r"; #[cfg(not(tarpaulin_include))] fn main() { + if std::env::args().len() > 1 { + let args = std::env::args().collect::>(); + + if args[1] == "--serBoard" && args.len() != 3 { + println!("usage: bamboo [--serBoard ]"); + std::process::exit(1); + } else if args[1] == "--serBoard" { + match Board::from_fen(args[2].as_str()) { + Ok(b) => { + println!("{}", hex::encode(b.to_board_id())); + std::process::exit(0); + }, + Err(e) => { + println!("Error decoding FEN string: {}", e); + std::process::exit(1); + } + } + } else { + println!("usage: bamboo [--serBoard ]"); + std::process::exit(1); + } + } + println!("{} {} by {}", ENGINE_NAME, ENGINE_VERSION, ENGINE_AUTHOR); let mut uci_state = UCIState::new(); diff --git a/src/piece.rs b/src/piece.rs index 2e872e5..0514069 100644 --- a/src/piece.rs +++ b/src/piece.rs @@ -1,4 +1,4 @@ -#[derive(Debug)] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] pub struct PieceOnBoard { pub loc: usize, pub value: Piece @@ -12,7 +12,7 @@ impl PieceOnBoard { } } -#[derive(Debug)] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] #[repr(u8)] pub enum PieceType { Pawn = 1, @@ -23,7 +23,7 @@ pub enum PieceType { King = 6 } -#[derive(Debug)] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] #[repr(u8)] pub enum PieceColor { White = 8, @@ -52,10 +52,15 @@ impl PieceOps for Piece { #[cfg(test)] mod piece_tests { - use crate::piece::{Piece, PieceColor, PieceOps, PieceType}; + use crate::piece::{Piece, PieceColor, PieceOnBoard, PieceOps, PieceType}; #[test] fn piece_serialization() { + assert_eq!(PieceOnBoard::new(0, 0), PieceOnBoard { + loc: 0, + value: 0, + }); + assert_eq!(PieceType::Pawn as u8 | PieceColor::White as u8, 0b1001); assert_eq!(PieceType::Pawn as u8 | PieceColor::Black as u8, 0b0001);