180 lines
No EOL
8.2 KiB
Rust
180 lines
No EOL
8.2 KiB
Rust
// 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 a 32-byte bitfield, to make accessing them as efficient as possible.
|
|
// We could theoretically pack it into two u128s, but some basic testing seems like LLVM can optimize array indexing faster than the bucketload of shift ops needed to pack it into u128s.
|
|
|
|
|
|
/*
|
|
|
|
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};
|
|
|
|
pub 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::boardfield::{Boardfield, BoardfieldOps};
|
|
use crate::piece::{PieceColor, PieceType};
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn get_out_of_bounds() {
|
|
let field = Boardfield::startpos();
|
|
field.get_pos(64);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn set_out_of_bounds() {
|
|
let mut field = Boardfield::startpos();
|
|
field.set_pos(64, 0u8);
|
|
}
|
|
|
|
#[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);
|
|
}
|
|
} |