// 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); } }