From 8a61a3aedca29eb84b5c07bdf1f0d473f7e92b59 Mon Sep 17 00:00:00 2001 From: c0repwn3r Date: Sun, 12 Feb 2023 18:55:40 -0500 Subject: [PATCH] bitfield boards are workin great --- src/board.rs | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 + src/piece.rs | 82 ++++++++++++++++++++++++++ src/uci.rs | 2 - 4 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 src/board.rs create mode 100644 src/piece.rs diff --git a/src/board.rs b/src/board.rs new file mode 100644 index 0000000..e3596ea --- /dev/null +++ b/src/board.rs @@ -0,0 +1,164 @@ +// Bitpacked board: +// One piece can be represented with 4 bits. Therefore, we can fit two pieces in one byte: +// 0b0110_1110 - +// | \ +// Black king \- White king +// As there are 64 spaces on the board, we can pack the board into a 256-bit integer, and we use that as the index for transposition tables and such. +// Boards are stored as two 128-bit bitfields, to make accessing them as efficient as possible. + +/* + + a b c d e f g h + +8 56 57 58 59 60 61 62 63 8 -+ +7 48 49 50 51 52 53 54 55 7 |- bf2 +6 40 41 42 43 44 45 46 47 6 | +5 32 33 34 35 36 37 38 39 5 -+ +4 24 25 26 27 28 29 30 31 4 -+ +3 16 17 18 19 20 21 22 23 3 | +2 8 9 10 11 12 13 14 15 2 |- bf1 +1 0 1 2 3 4 5 6 7 1 -+ + + a b c d e f g h + */ + +use crate::piece::{PieceColor, PieceType}; + +type Boardfield = [u8; 32]; +pub trait BoardfieldOps { + fn new() -> Self where Self: Sized; + fn startpos() -> Self where Self: Sized; + fn get_pos(&self, boardloc: usize) -> u8; + fn set_pos(&mut self, boardloc: usize, piece: u8); +} +impl BoardfieldOps for Boardfield { + fn new() -> Self where Self: Sized { + [0u8; 32] + } + + fn startpos() -> Self where Self: Sized { + let mut field = Self::new(); + + // a1-h1 (RNBQKBNR) + field.set_pos(0, PieceColor::White as u8 | PieceType::Rook as u8); + field.set_pos(1, PieceColor::White as u8 | PieceType::Knight as u8); + field.set_pos(2, PieceColor::White as u8 | PieceType::Bishop as u8); + field.set_pos(3, PieceColor::White as u8 | PieceType::Queen as u8); + field.set_pos(4, PieceColor::White as u8 | PieceType::King as u8); + field.set_pos(5, PieceColor::White as u8 | PieceType::Bishop as u8); + field.set_pos(6, PieceColor::White as u8 | PieceType::Knight as u8); + field.set_pos(7, PieceColor::White as u8 | PieceType::Rook as u8); + + // a2-h2 (PPPPPPPP) + field.set_pos(8, PieceColor::White as u8 | PieceType::Pawn as u8); + field.set_pos(9, PieceColor::White as u8 | PieceType::Pawn as u8); + field.set_pos(10, PieceColor::White as u8 | PieceType::Pawn as u8); + field.set_pos(11, PieceColor::White as u8 | PieceType::Pawn as u8); + field.set_pos(12, PieceColor::White as u8 | PieceType::Pawn as u8); + field.set_pos(13, PieceColor::White as u8 | PieceType::Pawn as u8); + field.set_pos(14, PieceColor::White as u8 | PieceType::Pawn as u8); + field.set_pos(15, PieceColor::White as u8 | PieceType::Pawn as u8); + + // a7-h7 (pppppppp) + field.set_pos(48, PieceColor::Black as u8 | PieceType::Pawn as u8); + field.set_pos(49, PieceColor::Black as u8 | PieceType::Pawn as u8); + field.set_pos(50, PieceColor::Black as u8 | PieceType::Pawn as u8); + field.set_pos(51, PieceColor::Black as u8 | PieceType::Pawn as u8); + field.set_pos(52, PieceColor::Black as u8 | PieceType::Pawn as u8); + field.set_pos(53, PieceColor::Black as u8 | PieceType::Pawn as u8); + field.set_pos(54, PieceColor::Black as u8 | PieceType::Pawn as u8); + field.set_pos(55, PieceColor::Black as u8 | PieceType::Pawn as u8); + + // a8-h8 (RNBQKBNR) + field.set_pos(56, PieceColor::Black as u8 | PieceType::Rook as u8); + field.set_pos(57, PieceColor::Black as u8 | PieceType::Knight as u8); + field.set_pos(58, PieceColor::Black as u8 | PieceType::Bishop as u8); + field.set_pos(59, PieceColor::Black as u8 | PieceType::Queen as u8); + field.set_pos(60, PieceColor::Black as u8 | PieceType::King as u8); + field.set_pos(61, PieceColor::Black as u8 | PieceType::Bishop as u8); + field.set_pos(62, PieceColor::Black as u8 | PieceType::Knight as u8); + field.set_pos(63, PieceColor::Black as u8 | PieceType::Rook as u8); + + + field + } + + fn get_pos(&self, boardloc: usize) -> u8 { + // bf1: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + // bf2: 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 + + if boardloc > 63 { + panic!("boardloc out of range"); + } + + let field = self[boardloc / 2]; + let shift = 4 * (boardloc % 2); + + (field & (0b1111 << shift)) >> shift + } + + fn set_pos(&mut self, boardloc: usize, piece: u8) { + if boardloc > 63 { + panic!("boardloc out of range"); + } + + let field = self[boardloc / 2]; + let shift = 4 * (boardloc % 2); + + // clear out the field + let bf_cleared = field & !(0b1111 << shift); + // then shift over the actual piece data + let piece_shifted = piece << shift; + // then OR them together + let bf_new = bf_cleared | piece_shifted; + + self[boardloc / 2] = bf_new; + } +} + +#[cfg(test)] +mod tests { + use crate::board::{Boardfield, BoardfieldOps}; + use crate::piece::{PieceColor, PieceType}; + + #[test] + fn bitfield_board() { + let field = Boardfield::startpos(); + + assert_eq!(field.get_pos(0), PieceType::Rook as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(1), PieceType::Knight as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(2), PieceType::Bishop as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(3), PieceType::Queen as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(4), PieceType::King as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(5), PieceType::Bishop as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(6), PieceType::Knight as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(7), PieceType::Rook as u8 | PieceColor::White as u8); + + assert_eq!(field.get_pos(8), PieceType::Pawn as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(9), PieceType::Pawn as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(10), PieceType::Pawn as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(11), PieceType::Pawn as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(12), PieceType::Pawn as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(13), PieceType::Pawn as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(14), PieceType::Pawn as u8 | PieceColor::White as u8); + assert_eq!(field.get_pos(15), PieceType::Pawn as u8 | PieceColor::White as u8); + + assert_eq!(field.get_pos(48), PieceType::Pawn as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(49), PieceType::Pawn as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(50), PieceType::Pawn as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(51), PieceType::Pawn as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(52), PieceType::Pawn as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(53), PieceType::Pawn as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(54), PieceType::Pawn as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(55), PieceType::Pawn as u8 | PieceColor::Black as u8); + + assert_eq!(field.get_pos(56), PieceType::Rook as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(57), PieceType::Knight as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(58), PieceType::Bishop as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(59), PieceType::Queen as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(60), PieceType::King as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(61), PieceType::Bishop as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(62), PieceType::Knight as u8 | PieceColor::Black as u8); + assert_eq!(field.get_pos(63), PieceType::Rook as u8 | PieceColor::Black as u8); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 3699e57..1660215 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ use crate::uci::{uci_handle_command, UCIState}; use spin::Mutex; pub mod uci; +pub mod board; +pub mod piece; pub const ENGINE_NAME: &str = "Bamboo"; pub const ENGINE_VERSION: &str = "1.0"; diff --git a/src/piece.rs b/src/piece.rs new file mode 100644 index 0000000..4500e66 --- /dev/null +++ b/src/piece.rs @@ -0,0 +1,82 @@ +#[repr(u8)] +pub enum PieceType { + Pawn = 1, + Knight = 2, + Bishop = 3, + Rook = 4, + Queen = 5, + King = 6 +} + +#[repr(u8)] +pub enum PieceColor { + White = 8, + Black = 0 +} + +pub type Piece = u8; +pub trait PieceOps { + fn is_white(value: u8) -> bool; + + fn is_black(value: u8) -> bool; +} +impl PieceOps for Piece { + fn is_white(value: u8) -> bool { + if value < 8 { + false + } else { + value & PieceColor::White as u8 != 0 + } + } + + fn is_black(value: u8) -> bool { + !Self::is_white(value) + } +} + +#[cfg(test)] +mod piece_tests { + use crate::piece::{Piece, PieceColor, PieceOps, PieceType}; + + #[test] + fn piece_serialization() { + assert_eq!(PieceType::Pawn as u8 | PieceColor::White as u8, 0b1001); + assert_eq!(PieceType::Pawn as u8 | PieceColor::Black as u8, 0b0001); + + assert_eq!(PieceType::Knight as u8 | PieceColor::White as u8, 0b1010); + assert_eq!(PieceType::Knight as u8 | PieceColor::Black as u8, 0b0010); + + assert_eq!(PieceType::Bishop as u8 | PieceColor::White as u8, 0b1011); + assert_eq!(PieceType::Bishop as u8 | PieceColor::Black as u8, 0b0011); + + assert_eq!(PieceType::Rook as u8 | PieceColor::White as u8, 0b1100); + assert_eq!(PieceType::Rook as u8 | PieceColor::Black as u8, 0b0100); + + assert_eq!(PieceType::Queen as u8 | PieceColor::White as u8, 0b1101); + assert_eq!(PieceType::Queen as u8 | PieceColor::Black as u8, 0b0101); + + assert_eq!(PieceType::King as u8 | PieceColor::White as u8, 0b1110); + assert_eq!(PieceType::King as u8 | PieceColor::Black as u8, 0b0110); + } + + #[test] + fn piece_color_checks() { + assert!(!Piece::is_white(PieceType::Pawn as u8 | PieceColor::Black as u8)); + assert!(Piece::is_white(PieceType::Pawn as u8 | PieceColor::White as u8)); + + assert!(!Piece::is_white(PieceType::Knight as u8 | PieceColor::Black as u8)); + assert!(Piece::is_white(PieceType::Knight as u8 | PieceColor::White as u8)); + + assert!(!Piece::is_white(PieceType::Bishop as u8 | PieceColor::Black as u8)); + assert!(Piece::is_white(PieceType::Bishop as u8 | PieceColor::White as u8)); + + assert!(!Piece::is_white(PieceType::Rook as u8 | PieceColor::Black as u8)); + assert!(Piece::is_white(PieceType::Rook as u8 | PieceColor::White as u8)); + + assert!(!Piece::is_white(PieceType::Queen as u8 | PieceColor::Black as u8)); + assert!(Piece::is_white(PieceType::Queen as u8 | PieceColor::White as u8)); + + assert!(!Piece::is_white(PieceType::King as u8 | PieceColor::Black as u8)); + assert!(Piece::is_white(PieceType::King as u8 | PieceColor::White as u8)); + } +} \ No newline at end of file diff --git a/src/uci.rs b/src/uci.rs index 4dcec17..2034111 100644 --- a/src/uci.rs +++ b/src/uci.rs @@ -1,8 +1,6 @@ use std::error::Error; use crate::{ENGINE_AUTHOR, ENGINE_NAME, ENGINE_VERSION}; use std::fmt::Write; -use std::fs; -use std::path::Path; use std::sync::{Arc}; use spin::Mutex;