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, /// /// 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, 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(&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 { 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::().unwrap_or(0); game.turn_number = turn_number.parse::().unwrap_or(0); Some(game) } pub fn from_fen_str(fen: &str) -> Option { let fen_parts = fen.split_whitespace(); Self::from_fen(fen_parts.collect::>().as_slice()) } pub fn parse_move(&self, mov: &str) -> Result { if mov.len() < 4 { Err("string too short".to_owned()) } 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, _ => return Err("invalid promotion".to_owned()) }) } else { None }; if let (Some(from), Some(to)) = (origin, target) { let (piece_type, side) = self.get_square(from).ok_or("no piece at from square")?; //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 { return Err("cannot capture own piece".to_owned()); } } if side != self.turn { return Err("wrong side to move".to_owned()); } if let Some(promote_to) = promote_to { if piece_type != PAWN { // should never happen return Err("cannot promote to pawn".to_owned()); } 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("no valid squares".to_owned()) } } } 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 { 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 { 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 } } }