|
@@ -0,0 +1,948 @@
|
|
|
|
+use bitboard::*;
|
|
|
|
+use movegen::*;
|
|
|
|
+use log::info;
|
|
|
|
+use std::sync::Arc;
|
|
|
|
+use zobrist::{ZobristTable, Hash};
|
|
|
|
+use crate::movegen;
|
|
|
|
+
|
|
|
|
+#[derive(Clone)]
|
|
|
|
+pub struct Board
|
|
|
|
+{
|
|
|
|
+ pub pieces: [Bitboard; 12],
|
|
|
|
+ pub turn: Side,
|
|
|
|
+
|
|
|
|
+ /// None if no en passant move is possible,
|
|
|
|
+ /// otherwise contains the file of the last (double-) pushed pawn
|
|
|
|
+ pub en_passant: Option<u8>,
|
|
|
|
+
|
|
|
|
+ ///
|
|
|
|
+ /// castling rights in the following order
|
|
|
|
+ /// white kingside, white queenside, black kingside, black queenside
|
|
|
|
+ ///
|
|
|
|
+ pub castling_rights: [bool; 4],
|
|
|
|
+
|
|
|
|
+ //
|
|
|
|
+ // non-hashed part
|
|
|
|
+ // vvv
|
|
|
|
+ pub turn_number: i32,
|
|
|
|
+ pub halfmoves_since_last_event: i8,
|
|
|
|
+ pub zobrist: Option<(Arc<ZobristTable>, Hash)>
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl Default for Board {
|
|
|
|
+ fn default () -> Board {
|
|
|
|
+ Board {
|
|
|
|
+ pieces: [
|
|
|
|
+ ROW_2,
|
|
|
|
+ 0x42,
|
|
|
|
+ 0x24,
|
|
|
|
+ 0x81,
|
|
|
|
+ 0x10,
|
|
|
|
+ 0x08,
|
|
|
|
+ ROW_7,
|
|
|
|
+ 0x42 << 56,
|
|
|
|
+ 0x24 << 56,
|
|
|
|
+ 0x81 << 56,
|
|
|
|
+ 0x10 << 56,
|
|
|
|
+ 0x08 << 56,
|
|
|
|
+ ],
|
|
|
|
+ turn: WHITE,
|
|
|
|
+ en_passant: None,
|
|
|
|
+ castling_rights: [true; 4],
|
|
|
|
+ halfmoves_since_last_event: 0,
|
|
|
|
+ turn_number: 0,
|
|
|
|
+ zobrist: None
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+impl std::hash::Hash for Board {
|
|
|
|
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
|
|
+ if let Some((ref _zt, ref zb)) = &self.zobrist {
|
|
|
|
+ zb.hash(state);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl PartialEq for Board {
|
|
|
|
+ fn eq(&self, other: &Self) -> bool {
|
|
|
|
+ self.pieces == other.pieces &&
|
|
|
|
+ self.turn == other.turn &&
|
|
|
|
+ self.en_passant == other.en_passant &&
|
|
|
|
+ self.castling_rights == other.castling_rights
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl Eq for Board {
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+impl Board {
|
|
|
|
+ pub fn empty() -> Board {
|
|
|
|
+ Board {
|
|
|
|
+ pieces: [0; 12],
|
|
|
|
+ turn: WHITE,
|
|
|
|
+ en_passant: None,
|
|
|
|
+ castling_rights: [true; 4],
|
|
|
|
+ halfmoves_since_last_event: 0,
|
|
|
|
+ turn_number: 0,
|
|
|
|
+ zobrist: None
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn from_fen(fen: &[&str]) -> Option<Board> {
|
|
|
|
+ let mut game: Board = Board::empty();
|
|
|
|
+ //let example = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
|
|
|
+
|
|
|
|
+ let position = fen[0];
|
|
|
|
+ let turn = fen[1];
|
|
|
|
+ let castling_rights = fen[2];
|
|
|
|
+ let en_passant = fen[3];
|
|
|
|
+ let halfmoves_since_last_event = fen[4];
|
|
|
|
+ let turn_number = fen[5];
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ let rows = position.split('/');
|
|
|
|
+ let mut row_index: i32 = 7;
|
|
|
|
+ for row in rows {
|
|
|
|
+ if row_index < 0 {
|
|
|
|
+ return None;
|
|
|
|
+ }
|
|
|
|
+ let mut col_index = 0;
|
|
|
|
+ for c in row.chars() {
|
|
|
|
+
|
|
|
|
+ if col_index >= 8 {
|
|
|
|
+ return None;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let bit_to_set = from_indices(col_index, row_index as _);
|
|
|
|
+ match c {
|
|
|
|
+ 'p' => { *game.pawns_mut(BLACK) |= bit_to_set },
|
|
|
|
+ 'n' => { *game.knights_mut(BLACK) |= bit_to_set },
|
|
|
|
+ 'b' => { *game.bishops_mut(BLACK) |= bit_to_set },
|
|
|
|
+ 'r' => { *game.rooks_mut(BLACK) |= bit_to_set },
|
|
|
|
+ 'q' => { *game.queens_mut(BLACK) |= bit_to_set },
|
|
|
|
+ 'k' => { *game.kings_mut(BLACK) |= bit_to_set },
|
|
|
|
+ 'P' => { *game.pawns_mut(WHITE) |= bit_to_set },
|
|
|
|
+ 'N' => { *game.knights_mut(WHITE) |= bit_to_set },
|
|
|
|
+ 'B' => { *game.bishops_mut(WHITE) |= bit_to_set },
|
|
|
|
+ 'R' => { *game.rooks_mut(WHITE) |= bit_to_set },
|
|
|
|
+ 'Q' => { *game.queens_mut(WHITE) |= bit_to_set },
|
|
|
|
+ 'K' => { *game.kings_mut(WHITE) |= bit_to_set },
|
|
|
|
+ num => {
|
|
|
|
+ col_index += match num.to_digit(10) {
|
|
|
|
+ Some(x) => x,
|
|
|
|
+ None => { return None; }
|
|
|
|
+ } as u8 - 1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ col_index += 1;
|
|
|
|
+ }
|
|
|
|
+ row_index -= 1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if turn == "b" {
|
|
|
|
+ game.turn = BLACK
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // parse castling rights
|
|
|
|
+ game.castling_rights[0] = castling_rights.contains('K');
|
|
|
|
+ game.castling_rights[1] = castling_rights.contains('Q');
|
|
|
|
+ game.castling_rights[2] = castling_rights.contains('k');
|
|
|
|
+ game.castling_rights[3] = castling_rights.contains('q');
|
|
|
|
+
|
|
|
|
+ // parse en passant
|
|
|
|
+ game.en_passant = Move::parse_square(en_passant).map(|sq| indices_from_square(sq).0);
|
|
|
|
+ //info!("en passant on file {:?}", game.en_passant);
|
|
|
|
+
|
|
|
|
+ game.halfmoves_since_last_event = halfmoves_since_last_event.parse::<i8>().unwrap_or(0);
|
|
|
|
+ game.turn_number = turn_number.parse::<i32>().unwrap_or(0);
|
|
|
|
+
|
|
|
|
+ Some(game)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn from_fen_str(fen: &str) -> Option<Board> {
|
|
|
|
+ let fen_parts = fen.split_whitespace();
|
|
|
|
+ Self::from_fen(fen_parts.collect::<Vec<&str>>().as_slice())
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn parse_move(&self, mov: &str) -> Result<Move, ()> {
|
|
|
|
+ if mov.len() < 4 {
|
|
|
|
+ Err(())
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ let origin = Move::parse_square(&mov[0..2]);
|
|
|
|
+ let target = Move::parse_square(&mov[2..4]);
|
|
|
|
+
|
|
|
|
+ let promote_to = if mov.len() == 5 {
|
|
|
|
+ Some(match mov.chars().nth(4) {
|
|
|
|
+ Some('Q') | Some('q') => QUEEN,
|
|
|
|
+ Some('N') | Some('n') => KNIGHT,
|
|
|
|
+ Some('B') | Some('b') => BISHOP,
|
|
|
|
+ Some('R') | Some('r') => ROOK,
|
|
|
|
+ _ => panic!("invalid move: {}", mov)
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ None
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ if let (Some(from), Some(to)) = (origin, target) {
|
|
|
|
+ let (piece_type, side) = self.get_square(from).unwrap_or(Err(())?);
|
|
|
|
+ //println!("from: {}", from);
|
|
|
|
+
|
|
|
|
+ let target = self.get_square(to);
|
|
|
|
+ let captured = if let Some((cap, _side)) = target { Some(cap) } else { None };
|
|
|
|
+
|
|
|
|
+ if let Some((_cap, cs)) = target {
|
|
|
|
+ if cs == side {
|
|
|
|
+ // cannot capture own piece
|
|
|
|
+ return Err(());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if side != self.turn { // wrong side to move
|
|
|
|
+ return Err(());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if let Some(promote_to) = promote_to {
|
|
|
|
+ if piece_type != PAWN {
|
|
|
|
+ return Err(());
|
|
|
|
+ }
|
|
|
|
+ Ok(Move::Promotion{ mov: SimpleMove{ from, to }, pc: PieceCaptureType::new(promote_to, captured) })
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ if piece_type == KING && (from as i32 - to as i32).abs() == 2 {
|
|
|
|
+ let left = from < to;
|
|
|
|
+ //println!("OMG castling!");
|
|
|
|
+ return Ok(Move::Castling{ mov: SimpleMove { from, to }, side, left });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // pawn capture
|
|
|
|
+ let (from_file, from_row) = indices_from_square(from);
|
|
|
|
+ let (target_file, _target_row) = indices_from_square(to);
|
|
|
|
+ if piece_type == PAWN && from_file != target_file {
|
|
|
|
+ let others = self.get_all_side(!side);
|
|
|
|
+ if others & from_square(to) == 0 {
|
|
|
|
+ let beaten = square_from_indices(target_file, from_row);
|
|
|
|
+ return Ok(Move::EnPassant{ mov: SimpleMove{ from, to }, beaten });
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //println!("pt: {}", piece_type);
|
|
|
|
+ Ok(Move::Default{ mov: SimpleMove{ from, to }, pc: PieceCaptureType::new(piece_type, captured) })
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ Err(())
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn bitboard(&self, pt: PieceType, side: Side) -> Bitboard {
|
|
|
|
+ if side == BLACK {
|
|
|
|
+ self.pieces[(pt + 6) as usize]
|
|
|
|
+ } else {
|
|
|
|
+ self.pieces[pt as usize]
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * finds the first bitboard that has overlapping bits with the given bitboard
|
|
|
|
+ * and returns the piece type of that bitboard
|
|
|
|
+ */
|
|
|
|
+ pub fn find_piece(&self, bitboard: Bitboard) -> Option<PieceType> {
|
|
|
|
+ for i in 0..12 {
|
|
|
|
+ if self.pieces[i] & bitboard != 0 {
|
|
|
|
+ return Some(if i >= 6 { i - 6 } else { i } as PieceType);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return None;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * \return bitboard containig all occupied squares
|
|
|
|
+ */
|
|
|
|
+ pub fn occupied(&self) -> Bitboard {
|
|
|
|
+ self.pieces.iter().fold(0, |a, b| { a | b } )
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn pawns(&self, side: Side) -> Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => self.pieces[0],
|
|
|
|
+ BLACK => self.pieces[6],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn knights(&self, side: Side) -> Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => self.pieces[1],
|
|
|
|
+ BLACK => self.pieces[7],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn bishops(&self, side: Side) -> Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => self.pieces[2],
|
|
|
|
+ BLACK => self.pieces[8],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn rooks(&self, side: Side) -> Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => self.pieces[3],
|
|
|
|
+ BLACK => self.pieces[9],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn queens(&self, side: Side) -> Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => self.pieces[4],
|
|
|
|
+ BLACK => self.pieces[10],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn kings(&self, side: Side) -> Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => self.pieces[5],
|
|
|
|
+ BLACK => self.pieces[11],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn pawns_mut(&mut self, side: Side) -> &mut Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => &mut self.pieces[0],
|
|
|
|
+ BLACK => &mut self.pieces[6],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn knights_mut(&mut self, side: Side) -> &mut Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => &mut self.pieces[1],
|
|
|
|
+ BLACK => &mut self.pieces[7],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn bishops_mut(&mut self, side: Side) -> &mut Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => &mut self.pieces[2],
|
|
|
|
+ BLACK => &mut self.pieces[8],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn rooks_mut(&mut self, side: Side) -> &mut Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => &mut self.pieces[3],
|
|
|
|
+ BLACK => &mut self.pieces[9],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn queens_mut(&mut self, side: Side) -> &mut Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => &mut self.pieces[4],
|
|
|
|
+ BLACK => &mut self.pieces[10],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn kings_mut(&mut self, side: Side) -> &mut Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => &mut self.pieces[5],
|
|
|
|
+ BLACK => &mut self.pieces[11],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn get_piece(&self, piece_type: PieceType, side: Side) -> Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => self.pieces[piece_type as usize],
|
|
|
|
+ BLACK => self.pieces[piece_type as usize + 6],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn set_piece(&mut self, piece_type: PieceType, side: Side, bitboard: Bitboard) {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => self.pieces[piece_type as usize] = bitboard,
|
|
|
|
+ BLACK => self.pieces[piece_type as usize + 6] = bitboard,
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn get_piece_mut(&mut self, piece_type: PieceType, side: Side) -> &mut Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => &mut self.pieces[piece_type as usize],
|
|
|
|
+ BLACK => &mut self.pieces[piece_type as usize + 6],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn get_all_side(&self, side: Side) -> Bitboard {
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => self.pieces[0] | self.pieces[1] | self.pieces[2] | self.pieces[3] |
|
|
|
|
+ self.pieces[4] | self.pieces[5],
|
|
|
|
+ BLACK => self.pieces[6] | self.pieces[7] | self.pieces[8] | self.pieces[9] |
|
|
|
|
+ self.pieces[10] | self.pieces[11]
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn get_square(&self, square: Square) -> Option<(PieceType, Side)> {
|
|
|
|
+ let square_mask = 1 << square;
|
|
|
|
+ for i in 0..6 {
|
|
|
|
+ if self.pieces[i] & square_mask != 0 {
|
|
|
|
+ return Some((i as PieceType, WHITE));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ for i in 0..6 {
|
|
|
|
+ if self.pieces[i + 6] & square_mask != 0 {
|
|
|
|
+ return Some((i as PieceType, BLACK));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return None;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @brief masks all bitboards.
|
|
|
|
+ */
|
|
|
|
+ pub fn apply_mask(&mut self, mask: Bitboard) {
|
|
|
|
+ for board in &mut self.pieces {
|
|
|
|
+ *board &= mask;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @brief creates a unicode string containing the board
|
|
|
|
+ *
|
|
|
|
+ * Example:
|
|
|
|
+ *
|
|
|
|
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┐
|
|
|
|
+ * 8 │ ♜ │ ♞ │ ♝ │ ♛ │ ♚ │ ♝ │ ♞ │ ♜ │
|
|
|
|
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
|
|
|
|
+ * 7 │ ♟ │ ♟ │ ♟ │ ♟ │ ♟ │ ♟ │ ♟ │ ♟ │
|
|
|
|
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
|
|
|
|
+ * 6 │ │ │ │ │ │ │ │ │
|
|
|
|
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
|
|
|
|
+ * 5 │ │ │ │ │ │ │ │ │
|
|
|
|
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
|
|
|
|
+ * 4 │ │ │ │ │ │ │ │ │
|
|
|
|
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
|
|
|
|
+ * 3 │ │ │ │ │ │ │ │ │
|
|
|
|
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
|
|
|
|
+ * 2 │ ♙ │ ♙ │ ♙ │ ♙ │ ♙ │ ♙ │ ♙ │ ♙ │
|
|
|
|
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
|
|
|
|
+ * 1 │ ♖ │ ♘ │ ♗ │ ♕ │ ♔ │ ♗ │ ♘ │ ♖ │
|
|
|
|
+ * └───┴───┴───┴───┴───┴───┴───┴───┘
|
|
|
|
+ * a b c d e f g h
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+ pub fn beautiful_print(&self) -> String {
|
|
|
|
+ let chars = ['─', '│', '┌', '┐', '└', '┘', '├', '┤', '┬', '┴', '┼'];
|
|
|
|
+ let figures = ['♙', '♘', '♗', '♖', '♕', '♔', '♟', '♞', '♝', '♜', '♛', '♚'];
|
|
|
|
+ let mut board: String = String::new();
|
|
|
|
+
|
|
|
|
+ #[derive(Copy, Clone)]
|
|
|
|
+ struct GridLine {
|
|
|
|
+ left: char,
|
|
|
|
+ center: char,
|
|
|
|
+ right: char,
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let top = GridLine { left: chars[2], center: chars[8], right: chars[3] };
|
|
|
|
+ let center = GridLine { left: chars[6], center: chars[10], right: chars[7] };
|
|
|
|
+ let bot = GridLine { left: chars[4], center: chars[9], right: chars[5] };
|
|
|
|
+
|
|
|
|
+ let generate_line = |gl: GridLine| {
|
|
|
|
+ let mut line = String::new();
|
|
|
|
+ for i in 0..33 {
|
|
|
|
+ match i {
|
|
|
|
+ 0 => { line.push(gl.left) }
|
|
|
|
+ 32 => { line.push(gl.right) }
|
|
|
|
+ x => { line.push(if x % 4 == 0 { gl.center } else { chars[0] }) }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return line;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let piece_line = |row: u8| {
|
|
|
|
+ let mut line = String::new();
|
|
|
|
+ line.push(chars[1]);
|
|
|
|
+ line.push(' ');
|
|
|
|
+ for i in 0..8 {
|
|
|
|
+ if let Some((pt, side)) = self.get_square(square_from_indices(i, 7 - row)) {
|
|
|
|
+ let fig_index = pt as usize + if side == BLACK { 6 } else { 0 };
|
|
|
|
+ line.push(figures[fig_index]);
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ line.push(' ');
|
|
|
|
+ }
|
|
|
|
+ line.push(' '); line.push(chars[1]); line.push(' ');
|
|
|
|
+ }
|
|
|
|
+ return line;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ board.push_str(" ");
|
|
|
|
+ board.push_str(&generate_line(top));
|
|
|
|
+ board.push('\n');
|
|
|
|
+ for i in 0..8 {
|
|
|
|
+ board.push_str(&(8 - i).to_string());
|
|
|
|
+ board.push(' ');
|
|
|
|
+ board.push_str(&piece_line(i));
|
|
|
|
+ board.push('\n');
|
|
|
|
+ if i != 7 {
|
|
|
|
+ board.push_str(" ");
|
|
|
|
+ board.push_str(&generate_line(center));
|
|
|
|
+ board.push('\n');
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ board.push_str(" ");
|
|
|
|
+ board.push_str(&generate_line(bot));
|
|
|
|
+ board.push_str("\n a b c d e f g h");
|
|
|
|
+
|
|
|
|
+ let chars = ['K', 'Q', 'k', 'q'];
|
|
|
|
+ let mut cr: [char; 4] = [' '; 4];
|
|
|
|
+ for i in 0..4 {
|
|
|
|
+ cr[i] = if self.castling_rights[i] {
|
|
|
|
+ chars[i]
|
|
|
|
+ } else { ' ' };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if self.turn == WHITE {
|
|
|
|
+ board.push_str("\nWhite to move\n");
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ board.push_str("\nBlack to move\n");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ board.push_str(&format!("\nCastling : {}{}{}{}", cr[0], cr[1], cr[2], cr[3]));
|
|
|
|
+ board.push_str(&format!("\nEn Passant : {}", self.en_passant.map(|f| f.to_string()).unwrap_or("-".to_owned())));
|
|
|
|
+ board.push_str(&format!("\nTurn number : {}", self.turn_number));
|
|
|
|
+ board.push_str(&format!("\nHalfmoves slt : {}", self.halfmoves_since_last_event));
|
|
|
|
+
|
|
|
|
+ return board;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn update_zobrist(&mut self, h: Hash) {
|
|
|
|
+ if let Some((ref _zt, ref mut zh)) = self.zobrist {
|
|
|
|
+ *zh ^= h;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn calculate_zobrist(&self, zt: &ZobristTable) -> Hash {
|
|
|
|
+ let mut hash: Hash = 0;
|
|
|
|
+ for pt in 0..6 {
|
|
|
|
+ let bb_it_w = BitboardIterator(self.get_piece(pt, WHITE));
|
|
|
|
+ let bb_it_b = BitboardIterator(self.get_piece(pt, BLACK));
|
|
|
|
+ for bb_square in bb_it_w {
|
|
|
|
+ hash ^= zt.piece_hash(pt as _, WHITE, square(bb_square));
|
|
|
|
+ }
|
|
|
|
+ for bb_square in bb_it_b {
|
|
|
|
+ hash ^= zt.piece_hash(pt as _, BLACK, square(bb_square));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for cr_id in 0..4 {
|
|
|
|
+ if self.castling_rights[cr_id] {
|
|
|
|
+ hash ^= zt.castling_rights_hash(cr_id as _);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if let Some(file) = self.en_passant {
|
|
|
|
+ hash ^= zt.en_passant_hash(file);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if self.turn {
|
|
|
|
+ hash ^= zt.turn_hash();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return hash;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn is_zobrist_correct(&self) -> bool {
|
|
|
|
+ if let Some((ref zt, zv)) = self.zobrist {
|
|
|
|
+ self.calculate_zobrist(zt) == zv
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ false
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ///
|
|
|
|
+ /// TODO: improve
|
|
|
|
+ ///
|
|
|
|
+ pub fn is_legal(&self, mov: Move) -> bool {
|
|
|
|
+ /*match mov {
|
|
|
|
+ Move::Default { mov, pc } => {
|
|
|
|
+ let piece_type = pc.piece_type();
|
|
|
|
+ let capture = pc.capture_type();
|
|
|
|
+
|
|
|
|
+ let legal_from = self.get_piece(piece_type, self.turn);
|
|
|
|
+
|
|
|
|
+ match piece_type {
|
|
|
|
+ PAWN => {
|
|
|
|
+ if pawn
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }*/
|
|
|
|
+ movegen::generate_legal_moves(&mut self.clone(), self.turn, false).contains(&mov)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn apply(&mut self, mov: Move) -> MoveUndo {
|
|
|
|
+ /*if !self.is_zobrist_correct() {
|
|
|
|
+ println!("{}", self.beautiful_print());
|
|
|
|
+ println!("incorrect zobrist before apply {} {:?}", mov.to_string(), mov);
|
|
|
|
+ info!("incorrect zobrist before apply");
|
|
|
|
+ panic!("zobrist");
|
|
|
|
+ let val = if let Some((ref zt, _zv)) = self.zobrist {
|
|
|
|
+ self.calculate_zobrist(zt)
|
|
|
|
+ } else { 0 };
|
|
|
|
+ if let Some((ref _zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ *zv = val;
|
|
|
|
+ }
|
|
|
|
+ }*/
|
|
|
|
+
|
|
|
|
+ // save irrecoverable values
|
|
|
|
+ let castling_rights_before = self.castling_rights.clone();
|
|
|
|
+ let halfmoves_since_last_event_before = self.halfmoves_since_last_event.clone();
|
|
|
|
+ let en_passant_before = self.en_passant;
|
|
|
|
+
|
|
|
|
+ let side = self.turn;
|
|
|
|
+ let others = self.get_all_side(!side);
|
|
|
|
+
|
|
|
|
+ if !mov.is_null() {
|
|
|
|
+ match mov {
|
|
|
|
+ Move::Default{ mov, pc } => {
|
|
|
|
+ let pt = pc.piece_type();
|
|
|
|
+
|
|
|
|
+ if pt == KING {
|
|
|
|
+ // invalidate castling rights
|
|
|
|
+ self.castling_rights[if side == BLACK { 2 } else { 0 }] = false;
|
|
|
|
+ self.castling_rights[if side == BLACK { 3 } else { 1 }] = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // invalidate castling rights
|
|
|
|
+ if mov.from == square_from_indices(7, 0) || mov.to == square_from_indices(7, 0) {
|
|
|
|
+ self.castling_rights[0] = false;
|
|
|
|
+ }
|
|
|
|
+ if mov.from == square_from_indices(0, 0) || mov.to == square_from_indices(0, 0) {
|
|
|
|
+ self.castling_rights[1] = false;
|
|
|
|
+ }
|
|
|
|
+ if mov.from == square_from_indices(7, 7) || mov.to == square_from_indices(7, 7) {
|
|
|
|
+ self.castling_rights[2] = false;
|
|
|
|
+ }
|
|
|
|
+ if mov.from == square_from_indices(0, 7) || mov.to == square_from_indices(0, 7) {
|
|
|
|
+ self.castling_rights[3] = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // if it is a capture
|
|
|
|
+ if from_square(mov.to) & others != 0 {
|
|
|
|
+ if let Some((other_piece, other_side)) = self.get_square(mov.to) {
|
|
|
|
+ // update zobrist
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let hup = zt.piece_hash(other_piece, other_side, mov.to);
|
|
|
|
+ *zv ^= hup;
|
|
|
|
+ }
|
|
|
|
+ *self.get_piece_mut(other_piece, other_side) &= !from_square(mov.to);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // reset en passant and then check if possible
|
|
|
|
+ self.en_passant = None;
|
|
|
|
+ if pt == PAWN {
|
|
|
|
+ let row1 = indices_from_square(mov.from).1;
|
|
|
|
+ let row2 = indices_from_square(mov.to).1;
|
|
|
|
+ // check for double push
|
|
|
|
+ if i32::abs(row1 as i32 - row2 as i32) >= 2 {
|
|
|
|
+ let opponent_pawns = self.get_piece(PAWN, !side);
|
|
|
|
+ let target_bb = from_square(mov.to);
|
|
|
|
+ if (west_one(target_bb) | east_one(target_bb)) & opponent_pawns != 0 {
|
|
|
|
+ self.en_passant = Some(indices_from_square(mov.to).0);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let moved_piece = mov.apply_to(self.get_piece(pt, side));
|
|
|
|
+ self.set_piece(pt, side, moved_piece);
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let hup = zt.piece_hash(pt, side, mov.from) ^ zt.piece_hash(pt, side, mov.to);
|
|
|
|
+ *zv ^= hup;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ Move::Castling { mov:_, side, left } => {
|
|
|
|
+ // invalidate future castling rights
|
|
|
|
+ self.castling_rights[if side == BLACK { 2 } else { 0 }] = false;
|
|
|
|
+ self.castling_rights[if side == BLACK { 3 } else { 1 }] = false;
|
|
|
|
+
|
|
|
|
+ let king = self.get_piece(KING, side);
|
|
|
|
+ let rook = if left {
|
|
|
|
+ self.get_piece(ROOK, side) & (king << 4)
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ self.get_piece(ROOK, side) & (king >> 3)
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let new_king = if left { king << 2 } else { king >> 2 };
|
|
|
|
+ let new_rook = if left { rook >> 3 } else { rook << 2 };
|
|
|
|
+
|
|
|
|
+ self.set_piece(KING, side, new_king);
|
|
|
|
+ *self.get_piece_mut(ROOK, side) &= !rook;
|
|
|
|
+ *self.get_piece_mut(ROOK, side) |= new_rook;
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let hupk = zt.piece_hash(KING, side, square(king)) ^ zt.piece_hash(KING, side, square(new_king));
|
|
|
|
+ let hup = zt.piece_hash(ROOK, side, square(rook)) ^ zt.piece_hash(ROOK, side, square(new_rook));
|
|
|
|
+ *zv ^= hup ^ hupk;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.en_passant = None;
|
|
|
|
+ },
|
|
|
|
+ Move::EnPassant { mov, beaten } => {
|
|
|
|
+ if let Some(_ep) = self.en_passant {
|
|
|
|
+ *self.get_piece_mut(PAWN, side) &= !from_square(mov.from);
|
|
|
|
+ *self.get_piece_mut(PAWN, side) |= from_square(mov.to);
|
|
|
|
+ *self.get_piece_mut(PAWN, !side) &= !from_square(beaten);
|
|
|
|
+
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let hup = zt.piece_hash(PAWN, side, mov.from) ^ zt.piece_hash(PAWN, side, mov.to);
|
|
|
|
+ let hupo = zt.piece_hash(PAWN, !side, beaten);
|
|
|
|
+ *zv ^= hup ^ hupo;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.en_passant = None;
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ info!("internal en passant error");
|
|
|
|
+ panic!("internal en passant error");
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ Move::Promotion { mov, pc } => {
|
|
|
|
+ let promote_to = pc.piece_type();
|
|
|
|
+ let captured = pc.capture_type();
|
|
|
|
+ //if from_square(mov.to) & others != 0 {
|
|
|
|
+ if let Some(pt) = captured {
|
|
|
|
+ *self.get_piece_mut(pt, !side) &= !from_square(mov.to);
|
|
|
|
+
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let hup = zt.piece_hash(pt, !side, mov.to);
|
|
|
|
+ *zv ^= hup;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ *self.get_piece_mut(PAWN, side) &= !from_square(mov.from);
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let hup = zt.piece_hash(PAWN, side, mov.from);
|
|
|
|
+ *zv ^= hup;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ match promote_to {
|
|
|
|
+ QUEEN => { *self.queens_mut(side) |= from_square(mov.to); }
|
|
|
|
+ ROOK => { *self.rooks_mut(side) |= from_square(mov.to); }
|
|
|
|
+ BISHOP => { *self.bishops_mut(side) |= from_square(mov.to); }
|
|
|
|
+ KNIGHT => { *self.knights_mut(side) |= from_square(mov.to); }
|
|
|
|
+ _ => {
|
|
|
|
+ info!("internal error");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let hup = zt.piece_hash(promote_to, side, mov.to);
|
|
|
|
+ *zv ^= hup;
|
|
|
|
+ }
|
|
|
|
+ self.en_passant = None;
|
|
|
|
+ },
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if self.turn == BLACK {
|
|
|
|
+ self.turn_number += 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.turn = !self.turn;
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let hup = zt.turn_hash();
|
|
|
|
+ let castling_hup = zt.all_castling_rights_hash(castling_rights_before) ^ zt.all_castling_rights_hash(self.castling_rights);
|
|
|
|
+ let enpass_hup = en_passant_before.map(|f| zt.en_passant_hash(f)).unwrap_or(0) ^ self.en_passant.map(|f| zt.en_passant_hash(f)).unwrap_or(0);
|
|
|
|
+ *zv ^= hup ^ castling_hup ^ enpass_hup;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*if !self.is_zobrist_correct() {
|
|
|
|
+ println!("{}", self.beautiful_print());
|
|
|
|
+ println!("incorrect zobrist after apply {} {:?}", mov.to_string(), mov);
|
|
|
|
+ info!("incorrect zobrist after apply");
|
|
|
|
+ panic!("zobrist");
|
|
|
|
+ let val = if let Some((ref zt, _zv)) = self.zobrist {
|
|
|
|
+ self.calculate_zobrist(zt)
|
|
|
|
+ } else { 0 };
|
|
|
|
+ if let Some((ref _zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ *zv = val;
|
|
|
|
+ }
|
|
|
|
+ }*/
|
|
|
|
+
|
|
|
|
+ MoveUndo {
|
|
|
|
+ castling_rights_before,
|
|
|
|
+ halfmoves_since_last_event_before,
|
|
|
|
+ en_passant_before,
|
|
|
|
+ mov
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ pub fn undo_move(&mut self, umov: MoveUndo) {
|
|
|
|
+
|
|
|
|
+ /*if !self.is_zobrist_correct() {
|
|
|
|
+ println!("{}", self.beautiful_print());
|
|
|
|
+ println!("incorrect zobrist before undo {} {:?}", umov.mov.to_string(), umov.mov);
|
|
|
|
+ info!("incorrect zobrist before undo {} {:?}", umov.mov.to_string(), umov.mov);
|
|
|
|
+ panic!("zobrist");
|
|
|
|
+ }*/
|
|
|
|
+
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let crhup = zt.all_castling_rights_hash(self.castling_rights) ^ zt.all_castling_rights_hash(umov.castling_rights_before);
|
|
|
|
+ let enpass_hup = self.en_passant.map(|f| zt.en_passant_hash(f)).unwrap_or(0) ^ umov.en_passant_before.map(|f| zt.en_passant_hash(f)).unwrap_or(0);
|
|
|
|
+ *zv ^= crhup ^ enpass_hup;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.castling_rights = umov.castling_rights_before;
|
|
|
|
+ self.halfmoves_since_last_event = umov.halfmoves_since_last_event_before;
|
|
|
|
+ self.en_passant = umov.en_passant_before;
|
|
|
|
+
|
|
|
|
+ let mov = umov.mov;
|
|
|
|
+
|
|
|
|
+ // the side that played the turn that is to be reverted
|
|
|
|
+ let side = !self.turn;
|
|
|
|
+
|
|
|
|
+ if !mov.is_null() {
|
|
|
|
+ match mov {
|
|
|
|
+ Move::Default{ mov, pc } => {
|
|
|
|
+ let piece_type = pc.piece_type();
|
|
|
|
+ let captured = pc.capture_type();
|
|
|
|
+
|
|
|
|
+ let moved_piece_bb = self.get_piece_mut(piece_type, side);
|
|
|
|
+ *moved_piece_bb &= !from_square(mov.to);
|
|
|
|
+ *moved_piece_bb |= from_square(mov.from);
|
|
|
|
+
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let hup = zt.piece_hash(piece_type, side, mov.from) ^ zt.piece_hash(piece_type, side, mov.to);
|
|
|
|
+ *zv ^= hup;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if let Some(pt) = captured {
|
|
|
|
+ *self.get_piece_mut(pt, !side) |= from_square(mov.to);
|
|
|
|
+
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let hup = zt.piece_hash(pt, !side, mov.to);
|
|
|
|
+ *zv ^= hup;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ Move::Castling { mov: _, side, left } => {
|
|
|
|
+ let king = self.get_piece(KING, side);
|
|
|
|
+
|
|
|
|
+ let rook = if left {
|
|
|
|
+ self.get_piece(ROOK, side) & (king >> 1)
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ self.get_piece(ROOK, side) & (king << 1)
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let old_king = if left { king >> 2 } else { king << 2 };
|
|
|
|
+ let old_rook = if left { rook << 3 } else { rook >> 2 };
|
|
|
|
+
|
|
|
|
+ self.set_piece(KING, side, old_king);
|
|
|
|
+ *self.get_piece_mut(ROOK, side) &= !rook;
|
|
|
|
+ *self.get_piece_mut(ROOK, side) |= old_rook;
|
|
|
|
+
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let khup = zt.piece_hash(KING, side, square(old_king)) ^ zt.piece_hash(KING, side, square(king));
|
|
|
|
+ let rhup = zt.piece_hash(ROOK, side, square(rook)) ^ zt.piece_hash(ROOK, side, square(old_rook));
|
|
|
|
+ *zv ^= khup ^ rhup;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ Move::EnPassant { mov, beaten } => {
|
|
|
|
+ *self.get_piece_mut(PAWN, side) |= from_square(mov.from);
|
|
|
|
+ *self.get_piece_mut(PAWN, side) &= !from_square(mov.to);
|
|
|
|
+ *self.get_piece_mut(PAWN, !side) |= from_square(beaten);
|
|
|
|
+
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let phup = zt.piece_hash(PAWN, side, mov.from) ^ zt.piece_hash(PAWN, side, mov.to);
|
|
|
|
+ let chup = zt.piece_hash(PAWN, !side, beaten);
|
|
|
|
+ *zv ^= phup ^ chup;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // should already be reverted
|
|
|
|
+ //self.en_passant = Some(indices_from_square(beaten).0);
|
|
|
|
+ },
|
|
|
|
+ Move::Promotion { mov, pc } => {
|
|
|
|
+ let promote_to = pc.piece_type();
|
|
|
|
+ let captured = pc.capture_type();
|
|
|
|
+
|
|
|
|
+ match promote_to {
|
|
|
|
+ QUEEN => { *self.queens_mut(side) &= !from_square(mov.to); }
|
|
|
|
+ ROOK => { *self.rooks_mut(side) &= !from_square(mov.to); }
|
|
|
|
+ BISHOP => { *self.bishops_mut(side) &= !from_square(mov.to); }
|
|
|
|
+ KNIGHT => { *self.knights_mut(side) &= !from_square(mov.to); }
|
|
|
|
+ _ => {
|
|
|
|
+ info!("internal error");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ *self.pawns_mut(side) |= from_square(mov.from);
|
|
|
|
+
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let hup = zt.piece_hash(PAWN, side, mov.from) ^ zt.piece_hash(promote_to, side, mov.to);
|
|
|
|
+ *zv ^= hup;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if let Some(pt) = captured {
|
|
|
|
+ *self.get_piece_mut(pt, !side) |= from_square(mov.to);
|
|
|
|
+
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ let hup = zt.piece_hash(pt, !side, mov.to);
|
|
|
|
+ *zv ^= hup;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.turn = side;
|
|
|
|
+ if side == BLACK {
|
|
|
|
+ self.turn_number -= 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if let Some((ref zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ *zv ^= zt.turn_hash();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*if !self.is_zobrist_correct() {
|
|
|
|
+ println!("{}", self.beautiful_print());
|
|
|
|
+ println!("incorrect zobrist after undo {} {:?}", umov.mov.to_string(), umov.mov);
|
|
|
|
+ info!("incorrect zobrist after undo {} {:?}", umov.mov.to_string(), umov.mov);
|
|
|
|
+ panic!("zobrist");
|
|
|
|
+ }*/
|
|
|
|
+
|
|
|
|
+ /*let val = if let Some((ref zt, _zv)) = self.zobrist {
|
|
|
|
+ self.calculate_zobrist(zt)
|
|
|
|
+ } else { 0 };
|
|
|
|
+ if let Some((ref _zt, ref mut zv)) = self.zobrist {
|
|
|
|
+ *zv = val;
|
|
|
|
+ }*/
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn to_vector(&self) -> Vec<f64> {
|
|
|
|
+ let mut v = Vec::new();
|
|
|
|
+ for side in [WHITE, BLACK] {
|
|
|
|
+ for pt in 0..6 {
|
|
|
|
+ for sq in 0..64 {
|
|
|
|
+ if self.get_square(sq) == Some((pt, side)) {
|
|
|
|
+ v.push(1f64);
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ v.push(0f64);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ v
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn ply_number(&self) -> i32 {
|
|
|
|
+ self.turn_number * 2 + if self.turn == BLACK { 1 } else { 0 }
|
|
|
|
+ }
|
|
|
|
+}
|