board: make all board types use bitboards
This commit is contained in:
parent
8485714a24
commit
ea58c72436
5
res/scripts/gen_squares.py
Executable file
5
res/scripts/gen_squares.py
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
for f in range(8):
|
||||||
|
for r in range(8):
|
||||||
|
print("pub const {}{}: Pos = {};".format(chr(f + 65), r + 1, f * 8 + r))
|
589
src/board.rs
589
src/board.rs
|
@ -1,261 +1,294 @@
|
||||||
//! Basic type definitions and functions.
|
//! Basic type definitions and functions.
|
||||||
|
|
||||||
// Piece type flags.
|
|
||||||
pub const SQ_E: u8 = 0;
|
|
||||||
pub const SQ_P: u8 = 0b00000001;
|
|
||||||
pub const SQ_B: u8 = 0b00000010;
|
|
||||||
pub const SQ_N: u8 = 0b00000100;
|
|
||||||
pub const SQ_R: u8 = 0b00001000;
|
|
||||||
pub const SQ_Q: u8 = 0b00010000;
|
|
||||||
pub const SQ_K: u8 = 0b00100000;
|
|
||||||
pub const SQ_TYPE_MASK: u8 = 0b00111111;
|
|
||||||
|
|
||||||
// Piece color flags.
|
|
||||||
pub const SQ_WH: u8 = 0b01000000;
|
|
||||||
pub const SQ_BL: u8 = 0b10000000;
|
|
||||||
pub const SQ_COLOR_MASK: u8 = 0b11000000;
|
|
||||||
|
|
||||||
// Piece flags helpers.
|
|
||||||
pub const SQ_WH_P: u8 = SQ_WH|SQ_P;
|
|
||||||
pub const SQ_WH_B: u8 = SQ_WH|SQ_B;
|
|
||||||
pub const SQ_WH_N: u8 = SQ_WH|SQ_N;
|
|
||||||
pub const SQ_WH_R: u8 = SQ_WH|SQ_R;
|
|
||||||
pub const SQ_WH_Q: u8 = SQ_WH|SQ_Q;
|
|
||||||
pub const SQ_WH_K: u8 = SQ_WH|SQ_K;
|
|
||||||
pub const SQ_BL_P: u8 = SQ_BL|SQ_P;
|
|
||||||
pub const SQ_BL_B: u8 = SQ_BL|SQ_B;
|
|
||||||
pub const SQ_BL_N: u8 = SQ_BL|SQ_N;
|
|
||||||
pub const SQ_BL_R: u8 = SQ_BL|SQ_R;
|
|
||||||
pub const SQ_BL_Q: u8 = SQ_BL|SQ_Q;
|
|
||||||
pub const SQ_BL_K: u8 = SQ_BL|SQ_K;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub const fn has_flag(i: u8, flag: u8) -> bool { i & flag == flag }
|
|
||||||
|
|
||||||
// Wrappers for clearer naming.
|
|
||||||
/// Get type of piece on square, without color.
|
|
||||||
#[inline]
|
|
||||||
pub const fn get_type(square: u8) -> u8 { square & SQ_TYPE_MASK }
|
|
||||||
/// Return true if the piece on this square is of type `piece_type`.
|
|
||||||
#[inline]
|
|
||||||
pub const fn is_type(square: u8, piece_type: u8) -> bool { get_type(square) == piece_type }
|
|
||||||
/// Return true if the piece on this square has this color.
|
|
||||||
#[inline]
|
|
||||||
pub const fn is_color(square: u8, color: u8) -> bool { has_flag(square, color) }
|
|
||||||
/// Return true if this square has a white piece.
|
|
||||||
#[inline]
|
|
||||||
pub const fn is_white(square: u8) -> bool { is_color(square, SQ_WH) }
|
|
||||||
/// Return true if this square has a black piece.
|
|
||||||
#[inline]
|
|
||||||
pub const fn is_black(square: u8) -> bool { is_color(square, SQ_BL) }
|
|
||||||
/// Return the color of the piece on this square.
|
|
||||||
#[inline]
|
|
||||||
pub const fn get_color(square: u8) -> u8 { square & SQ_COLOR_MASK }
|
|
||||||
/// Return true if the piece on this square is the same as `piece`.
|
|
||||||
#[inline]
|
|
||||||
pub const fn is_piece(square: u8, piece: u8) -> bool { has_flag(square, piece) }
|
|
||||||
|
|
||||||
/// Get opposite color.
|
|
||||||
#[inline]
|
|
||||||
pub const fn opposite(color: u8) -> u8 { color ^ SQ_COLOR_MASK }
|
|
||||||
|
|
||||||
/// Pretty-print a color.
|
|
||||||
pub fn color_to_string(color: u8) -> String {
|
|
||||||
match color {
|
|
||||||
SQ_WH => "white".to_string(),
|
|
||||||
SQ_BL => "black".to_string(),
|
|
||||||
_ => panic!("Unknown color {}", color),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Minimum allowed value for stored Pos components.
|
|
||||||
pub const POS_MIN: i8 = 0;
|
|
||||||
/// Maximum allowed value for stored Pos components.
|
|
||||||
pub const POS_MAX: i8 = 7;
|
|
||||||
/// Coords (file, rank) of a square on a board, both components are in [0, 7].
|
|
||||||
pub type Pos = (i8, i8);
|
|
||||||
|
|
||||||
/// Check if a Pos component is in the [0, 7] range.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_valid_pos_c(component: i8) -> bool { component >= 0 && component <= 7 }
|
|
||||||
|
|
||||||
/// Check if both `pos` components are valid.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_valid_pos(pos: Pos) -> bool { is_valid_pos_c(pos.0) && is_valid_pos_c(pos.1) }
|
|
||||||
|
|
||||||
/// Convert string coordinates to Pos.
|
|
||||||
///
|
|
||||||
/// `s` has to be valid UTF8, or the very least ASCII because chars
|
|
||||||
/// are interpreted as raw bytes, and lowercase.
|
|
||||||
#[inline]
|
|
||||||
pub const fn pos(s: &str) -> Pos {
|
|
||||||
let chars = s.as_bytes();
|
|
||||||
((chars[0] - 0x61) as i8, (chars[1] - 0x31) as i8)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return string coordinates from Pos.
|
|
||||||
pub fn pos_string(p: &Pos) -> String {
|
|
||||||
let mut bytes = [0u8; 2];
|
|
||||||
bytes[0] = (p.0 + 0x61) as u8;
|
|
||||||
bytes[1] = (p.1 + 0x31) as u8;
|
|
||||||
String::from_utf8_lossy(&bytes).to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Bitboard for color or piece bits.
|
/// Bitboard for color or piece bits.
|
||||||
pub type Bitboard = u64;
|
pub type Bitboard = u64;
|
||||||
|
|
||||||
const BB_WH: u8 = 0;
|
/// Color type, used to index `Board.color`.
|
||||||
const BB_BL: u8 = 1;
|
pub type Color = usize;
|
||||||
const BB_P: u8 = 0;
|
|
||||||
const BB_B: u8 = 1;
|
const WHITE: usize = 0;
|
||||||
const BB_N: u8 = 2;
|
const BLACK: usize = 1;
|
||||||
const BB_R: u8 = 3;
|
const NUM_COLORS: usize = 2;
|
||||||
const BB_Q: u8 = 4;
|
|
||||||
const BB_K: u8 = 5;
|
/// Get opposite color.
|
||||||
|
#[inline]
|
||||||
|
pub const fn opposite(color: Color) -> Color { color ^ 1 }
|
||||||
|
|
||||||
|
/// Pretty-print a color.
|
||||||
|
pub fn color_to_string(color: Color) -> String {
|
||||||
|
match color {
|
||||||
|
0 => "white".to_string(),
|
||||||
|
1 => "black".to_string(),
|
||||||
|
_ => panic!("Unknown color {}", color),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Piece type, used to index `Board.piece`.
|
||||||
|
pub type Piece = usize;
|
||||||
|
|
||||||
|
const PAWN: usize = 0;
|
||||||
|
const BISHOP: usize = 1;
|
||||||
|
const KNIGHT: usize = 2;
|
||||||
|
const ROOK: usize = 3;
|
||||||
|
const QUEEN: usize = 4;
|
||||||
|
const KING: usize = 5;
|
||||||
|
const NUM_PIECES: usize = 6;
|
||||||
|
|
||||||
|
/// Coords (file, rank) of a square on a board.
|
||||||
|
pub type Square = i8;
|
||||||
|
|
||||||
|
// Generated by gen_squares.py.
|
||||||
|
pub const A1: Square = 0;
|
||||||
|
pub const A2: Square = 1;
|
||||||
|
pub const A3: Square = 2;
|
||||||
|
pub const A4: Square = 3;
|
||||||
|
pub const A5: Square = 4;
|
||||||
|
pub const A6: Square = 5;
|
||||||
|
pub const A7: Square = 6;
|
||||||
|
pub const A8: Square = 7;
|
||||||
|
pub const B1: Square = 8;
|
||||||
|
pub const B2: Square = 9;
|
||||||
|
pub const B3: Square = 10;
|
||||||
|
pub const B4: Square = 11;
|
||||||
|
pub const B5: Square = 12;
|
||||||
|
pub const B6: Square = 13;
|
||||||
|
pub const B7: Square = 14;
|
||||||
|
pub const B8: Square = 15;
|
||||||
|
pub const C1: Square = 16;
|
||||||
|
pub const C2: Square = 17;
|
||||||
|
pub const C3: Square = 18;
|
||||||
|
pub const C4: Square = 19;
|
||||||
|
pub const C5: Square = 20;
|
||||||
|
pub const C6: Square = 21;
|
||||||
|
pub const C7: Square = 22;
|
||||||
|
pub const C8: Square = 23;
|
||||||
|
pub const D1: Square = 24;
|
||||||
|
pub const D2: Square = 25;
|
||||||
|
pub const D3: Square = 26;
|
||||||
|
pub const D4: Square = 27;
|
||||||
|
pub const D5: Square = 28;
|
||||||
|
pub const D6: Square = 29;
|
||||||
|
pub const D7: Square = 30;
|
||||||
|
pub const D8: Square = 31;
|
||||||
|
pub const E1: Square = 32;
|
||||||
|
pub const E2: Square = 33;
|
||||||
|
pub const E3: Square = 34;
|
||||||
|
pub const E4: Square = 35;
|
||||||
|
pub const E5: Square = 36;
|
||||||
|
pub const E6: Square = 37;
|
||||||
|
pub const E7: Square = 38;
|
||||||
|
pub const E8: Square = 39;
|
||||||
|
pub const F1: Square = 40;
|
||||||
|
pub const F2: Square = 41;
|
||||||
|
pub const F3: Square = 42;
|
||||||
|
pub const F4: Square = 43;
|
||||||
|
pub const F5: Square = 44;
|
||||||
|
pub const F6: Square = 45;
|
||||||
|
pub const F7: Square = 46;
|
||||||
|
pub const F8: Square = 47;
|
||||||
|
pub const G1: Square = 48;
|
||||||
|
pub const G2: Square = 49;
|
||||||
|
pub const G3: Square = 50;
|
||||||
|
pub const G4: Square = 51;
|
||||||
|
pub const G5: Square = 52;
|
||||||
|
pub const G6: Square = 53;
|
||||||
|
pub const G7: Square = 54;
|
||||||
|
pub const G8: Square = 55;
|
||||||
|
pub const H1: Square = 56;
|
||||||
|
pub const H2: Square = 57;
|
||||||
|
pub const H3: Square = 58;
|
||||||
|
pub const H4: Square = 59;
|
||||||
|
pub const H5: Square = 60;
|
||||||
|
pub const H6: Square = 61;
|
||||||
|
pub const H7: Square = 62;
|
||||||
|
pub const H8: Square = 63;
|
||||||
|
|
||||||
|
/// Get bit mask of `p` in a bitboard.
|
||||||
|
#[inline]
|
||||||
|
pub const fn bit_pos(square: Square) -> u64 { 1 << square }
|
||||||
|
|
||||||
|
/// Convert string coordinates to Square.
|
||||||
|
///
|
||||||
|
/// `s` has to be valid UTF8, or the very least ASCII because chars
|
||||||
|
/// are interpreted as raw bytes, and lowercase.
|
||||||
|
#[inline]
|
||||||
|
pub const fn sq_from_string(square: &str) -> Square {
|
||||||
|
let chars = square.as_bytes();
|
||||||
|
(chars[0] - 0x61) as i8 * 8 + (chars[1] - 0x31) as i8
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return string coordinates from Square.
|
||||||
|
pub fn sq_to_string(square: Square) -> String {
|
||||||
|
let mut bytes = [0u8; 2];
|
||||||
|
bytes[0] = ((square / 8) + 0x61) as u8;
|
||||||
|
bytes[1] = ((square % 8) + 0x31) as u8;
|
||||||
|
String::from_utf8_lossy(&bytes).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
/// Board representation with color/piece bitboards.
|
/// Board representation with color/piece bitboards.
|
||||||
|
#[derive(PartialEq)]
|
||||||
pub struct Board {
|
pub struct Board {
|
||||||
pub color: [Bitboard; 2],
|
pub colors: [Bitboard; 2],
|
||||||
pub piece: [Bitboard; 6],
|
pub pieces: [Bitboard; 6],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate the board of a new game.
|
// Factories.
|
||||||
pub const fn new() -> Board {
|
impl Board {
|
||||||
Board {
|
/// Generate the board of a new game.
|
||||||
color: [
|
pub const fn new() -> Board {
|
||||||
0b11000000_11000000_11000000_11000000_11000000_11000000_11000000_11000000,
|
Board {
|
||||||
0b00000011_00000011_00000011_00000011_00000011_00000011_00000011_00000011,
|
colors: [
|
||||||
],
|
0b11000000_11000000_11000000_11000000_11000000_11000000_11000000_11000000,
|
||||||
piece: [
|
0b00000011_00000011_00000011_00000011_00000011_00000011_00000011_00000011,
|
||||||
0b01000010_01000010_01000010_01000010_01000010_01000010_01000010_01000010,
|
],
|
||||||
0b00000000_00000000_10000001_00000000_00000000_10000001_00000000_00000000,
|
pieces: [
|
||||||
0b00000000_10000001_00000000_00000000_00000000_00000000_10000001_00000000,
|
0b01000010_01000010_01000010_01000010_01000010_01000010_01000010_01000010,
|
||||||
0b10000001_00000000_00000000_00000000_00000000_00000000_00000000_10000001,
|
0b00000000_00000000_10000001_00000000_00000000_10000001_00000000_00000000,
|
||||||
0b00000000_00000000_00000000_10000001_00000000_00000000_00000000_00000000,
|
0b00000000_10000001_00000000_00000000_00000000_00000000_10000001_00000000,
|
||||||
0b00000000_00000000_00000000_00000000_10000001_00000000_00000000_00000000,
|
0b10000001_00000000_00000000_00000000_00000000_00000000_00000000_10000001,
|
||||||
]
|
0b00000000_00000000_00000000_10000001_00000000_00000000_00000000_00000000,
|
||||||
}
|
0b00000000_00000000_00000000_00000000_10000001_00000000_00000000_00000000,
|
||||||
}
|
]
|
||||||
|
|
||||||
/// Generate an empty board.
|
|
||||||
pub const fn new_empty() -> Board {
|
|
||||||
Board {
|
|
||||||
color: [0; 2],
|
|
||||||
piece: [0; 6],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a board from a FEN placement string.
|
|
||||||
pub fn new_from_fen(fen: &str) -> Board {
|
|
||||||
let mut board = [SQ_E; 64];
|
|
||||||
let mut f = 0;
|
|
||||||
let mut r = 7;
|
|
||||||
for c in fen.chars() {
|
|
||||||
match c {
|
|
||||||
'r' => { set_square(&mut board, &(f, r), SQ_BL_R); f += 1 }
|
|
||||||
'n' => { set_square(&mut board, &(f, r), SQ_BL_N); f += 1 }
|
|
||||||
'b' => { set_square(&mut board, &(f, r), SQ_BL_B); f += 1 }
|
|
||||||
'q' => { set_square(&mut board, &(f, r), SQ_BL_Q); f += 1 }
|
|
||||||
'k' => { set_square(&mut board, &(f, r), SQ_BL_K); f += 1 }
|
|
||||||
'p' => { set_square(&mut board, &(f, r), SQ_BL_P); f += 1 }
|
|
||||||
'R' => { set_square(&mut board, &(f, r), SQ_WH_R); f += 1 }
|
|
||||||
'N' => { set_square(&mut board, &(f, r), SQ_WH_N); f += 1 }
|
|
||||||
'B' => { set_square(&mut board, &(f, r), SQ_WH_B); f += 1 }
|
|
||||||
'Q' => { set_square(&mut board, &(f, r), SQ_WH_Q); f += 1 }
|
|
||||||
'K' => { set_square(&mut board, &(f, r), SQ_WH_K); f += 1 }
|
|
||||||
'P' => { set_square(&mut board, &(f, r), SQ_WH_P); f += 1 }
|
|
||||||
'/' => { f = 0; r -= 1; }
|
|
||||||
d if d.is_digit(10) => { f += d.to_digit(10).unwrap() as i8 }
|
|
||||||
_ => break,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
board
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get value of the square at this position.
|
/// Generate an empty board.
|
||||||
#[inline]
|
pub const fn new_empty() -> Board {
|
||||||
pub const fn get_square(board: &Board, coords: &Pos) -> u8 {
|
Board {
|
||||||
board[(coords.0 * 8 + coords.1) as usize]
|
colors: [0; 2],
|
||||||
}
|
pieces: [0; 6],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Set a new value for the square at this position.
|
/// Generate a board from a FEN placement string.
|
||||||
#[inline]
|
pub fn new_from_fen(fen: &str) -> Board {
|
||||||
pub fn set_square(board: &mut Board, coords: &Pos, piece: u8) {
|
let mut board = Board::new_empty();
|
||||||
board[(coords.0 * 8 + coords.1) as usize] = piece;
|
let mut f = 0;
|
||||||
}
|
let mut r = 7;
|
||||||
|
for c in fen.chars() {
|
||||||
/// Set the square empty at this position.
|
match c {
|
||||||
#[inline]
|
'r' => { board.set_square(f * 8 + r, BLACK, ROOK); f += 1 }
|
||||||
pub fn clear_square(board: &mut Board, coords: &Pos) {
|
'n' => { board.set_square(f * 8 + r, BLACK, KNIGHT); f += 1 }
|
||||||
set_square(board, coords, SQ_E);
|
'b' => { board.set_square(f * 8 + r, BLACK, BISHOP); f += 1 }
|
||||||
}
|
'q' => { board.set_square(f * 8 + r, BLACK, QUEEN); f += 1 }
|
||||||
|
'k' => { board.set_square(f * 8 + r, BLACK, KING); f += 1 }
|
||||||
/// Move a piece from a position to another, clearing initial square.
|
'p' => { board.set_square(f * 8 + r, BLACK, PAWN); f += 1 }
|
||||||
#[inline]
|
'R' => { board.set_square(f * 8 + r, WHITE, ROOK); f += 1 }
|
||||||
pub fn move_piece(board: &mut Board, from: &Pos, to: &Pos) {
|
'N' => { board.set_square(f * 8 + r, WHITE, KNIGHT); f += 1 }
|
||||||
set_square(board, &to, get_square(board, &from));
|
'B' => { board.set_square(f * 8 + r, WHITE, BISHOP); f += 1 }
|
||||||
clear_square(board, &from);
|
'Q' => { board.set_square(f * 8 + r, WHITE, QUEEN); f += 1 }
|
||||||
}
|
'K' => { board.set_square(f * 8 + r, WHITE, KING); f += 1 }
|
||||||
|
'P' => { board.set_square(f * 8 + r, WHITE, PAWN); f += 1 }
|
||||||
/// Return true of the square at this position is empty.
|
'/' => { f = 0; r -= 1; }
|
||||||
#[inline]
|
d if d.is_digit(10) => { f += d.to_digit(10).unwrap() as i8 }
|
||||||
pub const fn is_empty(board: &Board, coords: &Pos) -> bool {
|
_ => break,
|
||||||
get_square(board, coords) == SQ_E
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return an iterator over the pieces of the board along with pos.
|
|
||||||
pub fn get_piece_iterator<'a>(board: &'a Board) -> Box<dyn Iterator<Item = (u8, Pos)> + 'a> {
|
|
||||||
Box::new(
|
|
||||||
board.iter().enumerate()
|
|
||||||
.filter(|(_, s)| **s != SQ_E)
|
|
||||||
.map(|(i, s)| (*s, ((i / 8) as i8, (i % 8) as i8)))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find the king of `color`.
|
|
||||||
pub fn find_king(board: &Board, color: u8) -> Option<Pos> {
|
|
||||||
for f in 0..8 {
|
|
||||||
for r in 0..8 {
|
|
||||||
let s = get_square(board, &(f, r));
|
|
||||||
if is_color(s, color) && is_piece(s, SQ_K) {
|
|
||||||
return Some((f, r))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
board
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Count number of pieces on board. Used for debugging.
|
impl Board {
|
||||||
pub fn num_pieces(board: &Board) -> u8 {
|
/// Get combined white/black pieces bitboard.
|
||||||
let mut count = 0;
|
#[inline]
|
||||||
for i in board.iter() {
|
pub fn combined(&self) -> Bitboard {
|
||||||
if *i != SQ_E {
|
self.colors[WHITE] | self.colors[BLACK]
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
count
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a text view of the board. Used for debugging.
|
/// Get color type at position. It must hold a piece!
|
||||||
pub fn draw(board: &Board, f: &mut dyn std::io::Write) {
|
pub fn get_color(&self, p: Square) -> Color {
|
||||||
for r in (0..8).rev() {
|
let bp = bit_pos(p);
|
||||||
let mut rank = String::with_capacity(8);
|
if (self.colors[WHITE] & bp) == 1 { WHITE }
|
||||||
for f in 0..8 {
|
else if (self.pieces[BLACK] & bp) == 1 { BLACK }
|
||||||
let s = get_square(board, &(f, r));
|
else { panic!("Empty square.") }
|
||||||
let piece =
|
}
|
||||||
if is_piece(s, SQ_P) { 'p' }
|
|
||||||
else if is_piece(s, SQ_B) { 'b' }
|
/// Get piece type at position. It must hold a piece!
|
||||||
else if is_piece(s, SQ_N) { 'n' }
|
pub fn get_piece(&self, p: Square) -> Piece {
|
||||||
else if is_piece(s, SQ_R) { 'r' }
|
let bp = bit_pos(p);
|
||||||
else if is_piece(s, SQ_Q) { 'q' }
|
if (self.pieces[PAWN] & bp) == 1 { PAWN }
|
||||||
else if is_piece(s, SQ_K) { 'k' }
|
else if (self.pieces[BISHOP] & bp) == 1 { BISHOP }
|
||||||
else { '.' };
|
else if (self.pieces[KNIGHT] & bp) == 1 { KNIGHT }
|
||||||
let piece = if is_color(s, SQ_WH) { piece.to_ascii_uppercase() } else { piece };
|
else if (self.pieces[ROOK] & bp) == 1 { ROOK }
|
||||||
rank.push(piece);
|
else if (self.pieces[QUEEN] & bp) == 1 { QUEEN }
|
||||||
}
|
else if (self.pieces[KING] & bp) == 1 { KING }
|
||||||
writeln!(f, "{} {}", r + 1, rank).unwrap();
|
else { panic!("Empty square.") }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a new value for the square at this position.
|
||||||
|
#[inline]
|
||||||
|
pub fn set_square(&mut self, p: Square, color: Color, piece: Piece) {
|
||||||
|
self.colors[color] |= bit_pos(p);
|
||||||
|
self.pieces[piece] |= bit_pos(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the square empty at this position.
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_square(&mut self, p: Square) {
|
||||||
|
for color in 0..NUM_COLORS { self.colors[color] &= !bit_pos(p); }
|
||||||
|
for piece in 0..NUM_PIECES { self.pieces[piece] &= !bit_pos(p); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move a piece from a position to another, clearing initial position.
|
||||||
|
#[inline]
|
||||||
|
pub fn move_square(&mut self, from: Square, to: Square) {
|
||||||
|
self.set_square(to, self.get_color(from), self.get_piece(from));
|
||||||
|
self.clear_square(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find position of this king.
|
||||||
|
pub fn find_king(&self, color: Color) -> Option<Square> {
|
||||||
|
let king_bb = self.colors[color] & self.pieces[KING];
|
||||||
|
for p in 0..64 {
|
||||||
|
if king_bb & bit_pos(p) == 1 {
|
||||||
|
return Some(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Debug only: count number of pieces on board.
|
||||||
|
pub fn num_pieces(&self) -> u8 {
|
||||||
|
let cbb = self.combined();
|
||||||
|
let mut count = 0;
|
||||||
|
while cbb > 0 {
|
||||||
|
count += cbb & 1;
|
||||||
|
cbb >>= 1;
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Debug only: write a text view of the board.
|
||||||
|
pub fn draw(&self, f: &mut dyn std::io::Write) {
|
||||||
|
let cbb = self.colors[WHITE] | self.colors[BLACK];
|
||||||
|
for r in (0..8).rev() {
|
||||||
|
let mut rank = String::with_capacity(8);
|
||||||
|
for f in 0..8 {
|
||||||
|
let p = f * 8 + r;
|
||||||
|
let bp = bit_pos(p);
|
||||||
|
let piece_char = if cbb & bp == 0 {
|
||||||
|
'.'
|
||||||
|
} else {
|
||||||
|
let (color, piece) = (self.get_color(p), self.get_piece(p));
|
||||||
|
let mut piece_char = match piece {
|
||||||
|
PAWN => 'p',
|
||||||
|
BISHOP => 'b',
|
||||||
|
KNIGHT => 'n',
|
||||||
|
ROOK => 'r',
|
||||||
|
QUEEN => 'q',
|
||||||
|
KING => 'k',
|
||||||
|
};
|
||||||
|
if color == WHITE {
|
||||||
|
let piece_char = piece_char.to_ascii_uppercase();
|
||||||
|
}
|
||||||
|
piece_char
|
||||||
|
};
|
||||||
|
rank.push(piece_char);
|
||||||
|
}
|
||||||
|
writeln!(f, "{} {}", r + 1, rank).unwrap();
|
||||||
|
}
|
||||||
|
write!(f, " abcdefgh").unwrap();
|
||||||
}
|
}
|
||||||
write!(f, " abcdefgh").unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -265,70 +298,72 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_opposite() {
|
fn test_opposite() {
|
||||||
assert_eq!(opposite(SQ_WH), SQ_BL);
|
assert_eq!(opposite(WHITE), BLACK);
|
||||||
assert_eq!(opposite(SQ_BL), SQ_WH);
|
assert_eq!(opposite(BLACK), WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pos() {
|
fn test_sq_from_string() {
|
||||||
assert_eq!(pos("a1"), (0, 0));
|
assert_eq!(sq_from_string("a1"), A1);
|
||||||
assert_eq!(pos("a2"), (0, 1));
|
assert_eq!(sq_from_string("a2"), A2);
|
||||||
assert_eq!(pos("a8"), (0, 7));
|
assert_eq!(sq_from_string("a8"), A8);
|
||||||
assert_eq!(pos("b1"), (1, 0));
|
assert_eq!(sq_from_string("b1"), B1);
|
||||||
assert_eq!(pos("h8"), (7, 7));
|
assert_eq!(sq_from_string("h8"), H8);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pos_string() {
|
fn test_sq_to_string() {
|
||||||
assert_eq!(pos_string(&(0, 0)), "a1");
|
assert_eq!(sq_to_string(A1), "a1");
|
||||||
assert_eq!(pos_string(&(0, 1)), "a2");
|
assert_eq!(sq_to_string(A2), "a2");
|
||||||
assert_eq!(pos_string(&(0, 7)), "a8");
|
assert_eq!(sq_to_string(A8), "a8");
|
||||||
assert_eq!(pos_string(&(7, 7)), "h8");
|
assert_eq!(sq_to_string(H8), "h8");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_new_from_fen() {
|
fn test_new_from_fen() {
|
||||||
let b1 = new();
|
let b1 = Board::new();
|
||||||
let b2 = new_from_fen(notation::FEN_START);
|
let b2 = Board::new_from_fen(notation::FEN_START);
|
||||||
assert!(eq(&b1, &b2));
|
assert!(b1 == b2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_square() {
|
fn test_get_color() {
|
||||||
let b = new();
|
let b = Board::new();
|
||||||
assert_eq!(get_square(&b, &pos("a1")), SQ_WH_R);
|
assert_eq!(b.get_color(A1), WHITE);
|
||||||
assert_eq!(get_square(&b, &pos("a2")), SQ_WH_P);
|
assert_eq!(b.get_color(A2), WHITE);
|
||||||
assert_eq!(get_square(&b, &pos("a3")), SQ_E);
|
assert_eq!(b.get_color(A7), BLACK);
|
||||||
|
assert_eq!(b.get_color(A8), BLACK);
|
||||||
assert_eq!(get_square(&b, &pos("a7")), SQ_BL_P);
|
assert_eq!(b.get_color(D1), WHITE);
|
||||||
assert_eq!(get_square(&b, &pos("a8")), SQ_BL_R);
|
assert_eq!(b.get_color(D8), BLACK);
|
||||||
|
assert_eq!(b.get_color(E1), WHITE);
|
||||||
assert_eq!(get_square(&b, &pos("d1")), SQ_WH_Q);
|
assert_eq!(b.get_color(E8), BLACK);
|
||||||
assert_eq!(get_square(&b, &pos("d8")), SQ_BL_Q);
|
|
||||||
assert_eq!(get_square(&b, &pos("e1")), SQ_WH_K);
|
|
||||||
assert_eq!(get_square(&b, &pos("e8")), SQ_BL_K);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_empty() {
|
fn test_get_piece() {
|
||||||
let b = new();
|
let b = Board::new();
|
||||||
assert_eq!(is_empty(&b, &pos("a1")), false);
|
assert_eq!(b.get_piece(A1), ROOK);
|
||||||
assert_eq!(is_empty(&b, &pos("a2")), false);
|
assert_eq!(b.get_piece(A2), PAWN);
|
||||||
assert_eq!(is_empty(&b, &pos("a3")), true);
|
assert_eq!(b.get_piece(A7), PAWN);
|
||||||
|
assert_eq!(b.get_piece(A8), ROOK);
|
||||||
|
assert_eq!(b.get_piece(D1), QUEEN);
|
||||||
|
assert_eq!(b.get_piece(D8), QUEEN);
|
||||||
|
assert_eq!(b.get_piece(E1), KING);
|
||||||
|
assert_eq!(b.get_piece(E8), KING);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_find_king() {
|
fn test_find_king() {
|
||||||
let b = new_empty();
|
let b = Board::new_empty();
|
||||||
assert_eq!(find_king(&b, SQ_WH), None);
|
assert_eq!(b.find_king(WHITE), None);
|
||||||
let b = new();
|
let b = Board::new();
|
||||||
assert_eq!(find_king(&b, SQ_WH), Some(pos("e1")));
|
assert_eq!(b.find_king(WHITE), Some(E1));
|
||||||
assert_eq!(find_king(&b, SQ_BL), Some(pos("e8")));
|
assert_eq!(b.find_king(BLACK), Some(E8));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_num_pieces() {
|
fn test_num_pieces() {
|
||||||
assert_eq!(num_pieces(&new_empty()), 0);
|
assert_eq!(Board::new_empty().num_pieces(), 0);
|
||||||
assert_eq!(num_pieces(&new()), 32);
|
assert_eq!(Board::new().num_pieces(), 32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue