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.
|
||||
|
||||
// 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.
|
||||
pub type Bitboard = u64;
|
||||
|
||||
const BB_WH: u8 = 0;
|
||||
const BB_BL: u8 = 1;
|
||||
const BB_P: u8 = 0;
|
||||
const BB_B: u8 = 1;
|
||||
const BB_N: u8 = 2;
|
||||
const BB_R: u8 = 3;
|
||||
const BB_Q: u8 = 4;
|
||||
const BB_K: u8 = 5;
|
||||
/// Color type, used to index `Board.color`.
|
||||
pub type Color = usize;
|
||||
|
||||
const WHITE: usize = 0;
|
||||
const BLACK: usize = 1;
|
||||
const NUM_COLORS: usize = 2;
|
||||
|
||||
/// 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.
|
||||
#[derive(PartialEq)]
|
||||
pub struct Board {
|
||||
pub color: [Bitboard; 2],
|
||||
pub piece: [Bitboard; 6],
|
||||
pub colors: [Bitboard; 2],
|
||||
pub pieces: [Bitboard; 6],
|
||||
}
|
||||
|
||||
/// Generate the board of a new game.
|
||||
pub const fn new() -> Board {
|
||||
Board {
|
||||
color: [
|
||||
0b11000000_11000000_11000000_11000000_11000000_11000000_11000000_11000000,
|
||||
0b00000011_00000011_00000011_00000011_00000011_00000011_00000011_00000011,
|
||||
],
|
||||
piece: [
|
||||
0b01000010_01000010_01000010_01000010_01000010_01000010_01000010_01000010,
|
||||
0b00000000_00000000_10000001_00000000_00000000_10000001_00000000_00000000,
|
||||
0b00000000_10000001_00000000_00000000_00000000_00000000_10000001_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,
|
||||
// Factories.
|
||||
impl Board {
|
||||
/// Generate the board of a new game.
|
||||
pub const fn new() -> Board {
|
||||
Board {
|
||||
colors: [
|
||||
0b11000000_11000000_11000000_11000000_11000000_11000000_11000000_11000000,
|
||||
0b00000011_00000011_00000011_00000011_00000011_00000011_00000011_00000011,
|
||||
],
|
||||
pieces: [
|
||||
0b01000010_01000010_01000010_01000010_01000010_01000010_01000010_01000010,
|
||||
0b00000000_00000000_10000001_00000000_00000000_10000001_00000000_00000000,
|
||||
0b00000000_10000001_00000000_00000000_00000000_00000000_10000001_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,
|
||||
]
|
||||
}
|
||||
}
|
||||
board
|
||||
}
|
||||
|
||||
/// Get value of the square at this position.
|
||||
#[inline]
|
||||
pub const fn get_square(board: &Board, coords: &Pos) -> u8 {
|
||||
board[(coords.0 * 8 + coords.1) as usize]
|
||||
}
|
||||
/// Generate an empty board.
|
||||
pub const fn new_empty() -> Board {
|
||||
Board {
|
||||
colors: [0; 2],
|
||||
pieces: [0; 6],
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a new value for the square at this position.
|
||||
#[inline]
|
||||
pub fn set_square(board: &mut Board, coords: &Pos, piece: u8) {
|
||||
board[(coords.0 * 8 + coords.1) as usize] = piece;
|
||||
}
|
||||
|
||||
/// Set the square empty at this position.
|
||||
#[inline]
|
||||
pub fn clear_square(board: &mut Board, coords: &Pos) {
|
||||
set_square(board, coords, SQ_E);
|
||||
}
|
||||
|
||||
/// Move a piece from a position to another, clearing initial square.
|
||||
#[inline]
|
||||
pub fn move_piece(board: &mut Board, from: &Pos, to: &Pos) {
|
||||
set_square(board, &to, get_square(board, &from));
|
||||
clear_square(board, &from);
|
||||
}
|
||||
|
||||
/// Return true of the square at this position is empty.
|
||||
#[inline]
|
||||
pub const fn is_empty(board: &Board, coords: &Pos) -> bool {
|
||||
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))
|
||||
/// Generate a board from a FEN placement string.
|
||||
pub fn new_from_fen(fen: &str) -> Board {
|
||||
let mut board = Board::new_empty();
|
||||
let mut f = 0;
|
||||
let mut r = 7;
|
||||
for c in fen.chars() {
|
||||
match c {
|
||||
'r' => { board.set_square(f * 8 + r, BLACK, ROOK); f += 1 }
|
||||
'n' => { board.set_square(f * 8 + r, BLACK, KNIGHT); f += 1 }
|
||||
'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 }
|
||||
'p' => { board.set_square(f * 8 + r, BLACK, PAWN); f += 1 }
|
||||
'R' => { board.set_square(f * 8 + r, WHITE, ROOK); f += 1 }
|
||||
'N' => { board.set_square(f * 8 + r, WHITE, KNIGHT); f += 1 }
|
||||
'B' => { board.set_square(f * 8 + r, WHITE, BISHOP); f += 1 }
|
||||
'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 }
|
||||
'/' => { f = 0; r -= 1; }
|
||||
d if d.is_digit(10) => { f += d.to_digit(10).unwrap() as i8 }
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
board
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Count number of pieces on board. Used for debugging.
|
||||
pub fn num_pieces(board: &Board) -> u8 {
|
||||
let mut count = 0;
|
||||
for i in board.iter() {
|
||||
if *i != SQ_E {
|
||||
count += 1;
|
||||
}
|
||||
impl Board {
|
||||
/// Get combined white/black pieces bitboard.
|
||||
#[inline]
|
||||
pub fn combined(&self) -> Bitboard {
|
||||
self.colors[WHITE] | self.colors[BLACK]
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/// Write a text view of the board. Used for debugging.
|
||||
pub fn draw(board: &Board, f: &mut dyn std::io::Write) {
|
||||
for r in (0..8).rev() {
|
||||
let mut rank = String::with_capacity(8);
|
||||
for f in 0..8 {
|
||||
let s = get_square(board, &(f, r));
|
||||
let piece =
|
||||
if is_piece(s, SQ_P) { 'p' }
|
||||
else if is_piece(s, SQ_B) { 'b' }
|
||||
else if is_piece(s, SQ_N) { 'n' }
|
||||
else if is_piece(s, SQ_R) { 'r' }
|
||||
else if is_piece(s, SQ_Q) { 'q' }
|
||||
else if is_piece(s, SQ_K) { 'k' }
|
||||
else { '.' };
|
||||
let piece = if is_color(s, SQ_WH) { piece.to_ascii_uppercase() } else { piece };
|
||||
rank.push(piece);
|
||||
}
|
||||
writeln!(f, "{} {}", r + 1, rank).unwrap();
|
||||
/// Get color type at position. It must hold a piece!
|
||||
pub fn get_color(&self, p: Square) -> Color {
|
||||
let bp = bit_pos(p);
|
||||
if (self.colors[WHITE] & bp) == 1 { WHITE }
|
||||
else if (self.pieces[BLACK] & bp) == 1 { BLACK }
|
||||
else { panic!("Empty square.") }
|
||||
}
|
||||
|
||||
/// Get piece type at position. It must hold a piece!
|
||||
pub fn get_piece(&self, p: Square) -> Piece {
|
||||
let bp = bit_pos(p);
|
||||
if (self.pieces[PAWN] & bp) == 1 { PAWN }
|
||||
else if (self.pieces[BISHOP] & bp) == 1 { BISHOP }
|
||||
else if (self.pieces[KNIGHT] & bp) == 1 { KNIGHT }
|
||||
else if (self.pieces[ROOK] & bp) == 1 { ROOK }
|
||||
else if (self.pieces[QUEEN] & bp) == 1 { QUEEN }
|
||||
else if (self.pieces[KING] & bp) == 1 { KING }
|
||||
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)]
|
||||
|
@ -265,70 +298,72 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_opposite() {
|
||||
assert_eq!(opposite(SQ_WH), SQ_BL);
|
||||
assert_eq!(opposite(SQ_BL), SQ_WH);
|
||||
assert_eq!(opposite(WHITE), BLACK);
|
||||
assert_eq!(opposite(BLACK), WHITE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pos() {
|
||||
assert_eq!(pos("a1"), (0, 0));
|
||||
assert_eq!(pos("a2"), (0, 1));
|
||||
assert_eq!(pos("a8"), (0, 7));
|
||||
assert_eq!(pos("b1"), (1, 0));
|
||||
assert_eq!(pos("h8"), (7, 7));
|
||||
fn test_sq_from_string() {
|
||||
assert_eq!(sq_from_string("a1"), A1);
|
||||
assert_eq!(sq_from_string("a2"), A2);
|
||||
assert_eq!(sq_from_string("a8"), A8);
|
||||
assert_eq!(sq_from_string("b1"), B1);
|
||||
assert_eq!(sq_from_string("h8"), H8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pos_string() {
|
||||
assert_eq!(pos_string(&(0, 0)), "a1");
|
||||
assert_eq!(pos_string(&(0, 1)), "a2");
|
||||
assert_eq!(pos_string(&(0, 7)), "a8");
|
||||
assert_eq!(pos_string(&(7, 7)), "h8");
|
||||
fn test_sq_to_string() {
|
||||
assert_eq!(sq_to_string(A1), "a1");
|
||||
assert_eq!(sq_to_string(A2), "a2");
|
||||
assert_eq!(sq_to_string(A8), "a8");
|
||||
assert_eq!(sq_to_string(H8), "h8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_from_fen() {
|
||||
let b1 = new();
|
||||
let b2 = new_from_fen(notation::FEN_START);
|
||||
assert!(eq(&b1, &b2));
|
||||
let b1 = Board::new();
|
||||
let b2 = Board::new_from_fen(notation::FEN_START);
|
||||
assert!(b1 == b2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_square() {
|
||||
let b = new();
|
||||
assert_eq!(get_square(&b, &pos("a1")), SQ_WH_R);
|
||||
assert_eq!(get_square(&b, &pos("a2")), SQ_WH_P);
|
||||
assert_eq!(get_square(&b, &pos("a3")), SQ_E);
|
||||
|
||||
assert_eq!(get_square(&b, &pos("a7")), SQ_BL_P);
|
||||
assert_eq!(get_square(&b, &pos("a8")), SQ_BL_R);
|
||||
|
||||
assert_eq!(get_square(&b, &pos("d1")), SQ_WH_Q);
|
||||
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);
|
||||
fn test_get_color() {
|
||||
let b = Board::new();
|
||||
assert_eq!(b.get_color(A1), WHITE);
|
||||
assert_eq!(b.get_color(A2), WHITE);
|
||||
assert_eq!(b.get_color(A7), BLACK);
|
||||
assert_eq!(b.get_color(A8), BLACK);
|
||||
assert_eq!(b.get_color(D1), WHITE);
|
||||
assert_eq!(b.get_color(D8), BLACK);
|
||||
assert_eq!(b.get_color(E1), WHITE);
|
||||
assert_eq!(b.get_color(E8), BLACK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_empty() {
|
||||
let b = new();
|
||||
assert_eq!(is_empty(&b, &pos("a1")), false);
|
||||
assert_eq!(is_empty(&b, &pos("a2")), false);
|
||||
assert_eq!(is_empty(&b, &pos("a3")), true);
|
||||
fn test_get_piece() {
|
||||
let b = Board::new();
|
||||
assert_eq!(b.get_piece(A1), ROOK);
|
||||
assert_eq!(b.get_piece(A2), PAWN);
|
||||
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]
|
||||
fn test_find_king() {
|
||||
let b = new_empty();
|
||||
assert_eq!(find_king(&b, SQ_WH), None);
|
||||
let b = new();
|
||||
assert_eq!(find_king(&b, SQ_WH), Some(pos("e1")));
|
||||
assert_eq!(find_king(&b, SQ_BL), Some(pos("e8")));
|
||||
let b = Board::new_empty();
|
||||
assert_eq!(b.find_king(WHITE), None);
|
||||
let b = Board::new();
|
||||
assert_eq!(b.find_king(WHITE), Some(E1));
|
||||
assert_eq!(b.find_king(BLACK), Some(E8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_num_pieces() {
|
||||
assert_eq!(num_pieces(&new_empty()), 0);
|
||||
assert_eq!(num_pieces(&new()), 32);
|
||||
assert_eq!(Board::new_empty().num_pieces(), 0);
|
||||
assert_eq!(Board::new().num_pieces(), 32);
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue