فهرست منبع

improved movegen still with backup

Nicolas Winkler 3 سال پیش
والد
کامیت
472e8afd1b
6فایلهای تغییر یافته به همراه368 افزوده شده و 82 حذف شده
  1. 1 1
      src/bitboard.rs
  2. 1 1
      src/engine.rs
  3. 220 5
      src/game.rs
  4. 2 0
      src/main.rs
  5. 57 49
      src/movegen.rs
  6. 87 26
      src/search.rs

+ 1 - 1
src/bitboard.rs

@@ -100,7 +100,7 @@ pub fn from_indices(file: u8, row: u8) -> Bitboard {
 }
 
 ///
-/// reverse of indices_from_square
+/// reverse of indices_from_square: (file, row) from encoded square
 /// 
 pub fn indices_from_square(sq: Square) -> (u8, u8) {
     (7 - sq % 8, sq / 8)

+ 1 - 1
src/engine.rs

@@ -107,7 +107,7 @@ impl Engine {
                 };
                 let mut sc = SearchControl{ nodes: 0, check: &mut check_fn };
                 let before = Instant::now();
-                let search_result = search(&self.game, &mut sc, &mut self.hash, *depth as i32);
+                let search_result = search(&mut self.game, &mut sc, &mut self.hash, *depth as i32);
 
                 if let SearchResult::Finished(best_move, best_val) = search_result {
                     let time = before.elapsed();

+ 220 - 5
src/game.rs

@@ -12,7 +12,7 @@ pub struct Game
     pub turn: Side,
 
     /// None if no en passant move is possible,
-    /// otherwise contains the column of the last (double-) pushed pawn
+    /// otherwise contains the file of the last (double-) pushed pawn
     pub en_passant: Option<u8>,
 
     ///
@@ -28,7 +28,7 @@ pub struct Game
 
 
     pub turn_number: i32,
-    pub halfmoves_since_last_event: i32,
+    pub halfmoves_since_last_event: i8,
     pub zobrist: Option<(Arc<ZobristTable>, zobrist::Hash)>
 }
 
@@ -159,7 +159,7 @@ impl Game {
         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::<i32>().unwrap_or(0);
+        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)
@@ -197,7 +197,14 @@ impl Game {
                 }
                 else {
                     //println!("pt: {}", piece_type);
-                    Ok(Move::Default{ mov: SimpleMove{ from, to }, piece_type })
+                    let (cap_piece, cap_side) = self.get_square(to);
+
+                    if cap_piece == NO_PIECE {
+                        Ok(Move::Default{ mov: SimpleMove{ from, to }, piece_type, captured: None })
+                    }
+                    else {
+                        Ok(Move::Default{ mov: SimpleMove{ from, to }, piece_type, captured: Some(cap_piece) })
+                    }
                 }
             }
             else {
@@ -215,6 +222,19 @@ impl Game {
     }
 
     /**
+     * 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 {
@@ -417,7 +437,7 @@ impl Game {
             line.push(chars[1]);
             line.push(' ');
             for i in 0..8 {
-                let (pt, side) = self.get_square(square_from_indices(i, row));
+                let (pt, side) = self.get_square(square_from_indices(i, 7 - row));
                 if pt == NO_PIECE {
                     line.push(' ');
                 }
@@ -448,6 +468,26 @@ impl Game {
         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;
     }
 
@@ -489,5 +529,180 @@ impl Game {
 
     pub fn apply(&mut self, mov: Move) -> MoveUndo {
 
+        // 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 friends = self.get_all_side(side);
+        let others = self.get_all_side(!side);
+
+        match mov {
+            Move::Default{ mov, piece_type: pt, captured: _ } => {
+
+                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 {
+                    let (other_piece, other_side) = self.get_square(mov.to);
+                    if let Some((ref zt, ref _zv)) = self.zobrist {
+                        let hup = zt.piece_hash(other_piece, other_side, mov.to);
+                        self.update_zobrist(hup);
+                    }
+                    *self.get_piece_mut(other_piece, other_side) &= !from_square(mov.to);
+                }
+
+                let moved_piece = mov.apply_to(self.get_piece(pt, side));
+                self.set_piece(pt, side, moved_piece);
+
+                if let Some((ref zt, ref _zv)) = self.zobrist {
+                    let hup = zt.piece_hash(pt, side, mov.from) ^ zt.piece_hash(pt, side, mov.to);
+                    self.update_zobrist(hup);
+                }
+            },
+            Move::Castling { 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;
+            },
+            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);
+                }
+            },
+            Move::Promotion { mov, promote_to, captured } => {
+                //if from_square(mov.to) & others != 0 {
+                if let Some(pt) = captured {
+                    *self.get_piece_mut(pt, !side) &= !from_square(mov.to);
+                }
+                self.apply_mask(!from_square(mov.from));
+
+                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 self.turn == BLACK {
+            self.turn_number += 1;
+        }
+        self.turn = !self.turn;
+
+        MoveUndo {
+            castling_rights_before,
+            halfmoves_since_last_event_before,
+            en_passant_before,
+            mov
+        }
+    }
+
+    pub fn undo_move(&mut self, umov: MoveUndo) {
+        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;
+        match mov {
+            Move::Default{ mov, piece_type, captured } => {
+                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(pt) = captured {
+                    *self.get_piece_mut(pt, !side) |= from_square(mov.to);
+                }
+            },
+            Move::Castling { 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;
+            },
+            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);
+
+                // should already be reverted
+                //self.en_passant = Some(indices_from_square(beaten).0);
+            },
+            Move::Promotion { mov, promote_to, captured } => {
+                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(pt) = captured {
+                    *self.get_piece_mut(pt, !side) |= from_square(mov.to);
+                }
+            }
+        }
+
+        self.turn = side;
+        if side == BLACK {
+            self.turn_number -= 1;
+        }
     }
 }

+ 2 - 0
src/main.rs

@@ -28,6 +28,8 @@ fn main() {
         .init();
         */
     
+    //assert_eq!(std::mem::size_of::<crate::movegen::Move>(), 6);
+
     let logfile = File::create("/home/nicolas/debug.log").unwrap();
     simplelog::WriteLogger::init(LevelFilter::Info, Config::default(), logfile).unwrap();
 

+ 57 - 49
src/movegen.rs

@@ -26,32 +26,28 @@ pub struct SimpleMove {
     pub to: Square,
 }
 
+struct MovePieceTypes(u8);
+
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum Move {
-    Default { mov: SimpleMove, piece_type: PieceType },
+    Default { mov: SimpleMove, piece_type: PieceType, captured: Option<PieceType> },
     Castling { side: Side, left: bool },
     EnPassant { mov: SimpleMove, beaten: Square },
-    Promotion { mov: SimpleMove, promote_to: PieceType },
+    Promotion { mov: SimpleMove, promote_to: PieceType, captured: Option<PieceType> },
 }
 
-enum MoveUndo {
-    Default {
-        piece_type: PieceType,
-        before: Bitboard,
-        captured_type: PieceType,
-        captured_before: Bitboard,
-    },
-    Castling { rooks_before: Bitboard, king_before: Bitboard },
-    Promotion {
-        promoted_to: PieceType,
-        promoted_to_before: Bitboard,
-        pawns_before: Bitboard
-    }
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct MoveUndo {
+    pub castling_rights_before: [bool; 4],
+    pub halfmoves_since_last_event_before: i8,
+    pub en_passant_before: Option<u8>,
+    pub mov: Move
 }
 
 impl Default for Move {
     fn default() -> Move {
-        Move::Default{ mov: SimpleMove { from: 0, to: 0 }, piece_type: NO_PIECE }
+        Move::Default{ mov: SimpleMove { from: 0, to: 0 }, piece_type: NO_PIECE, captured: None }
     }
 }
 
@@ -79,7 +75,7 @@ impl Move {
 
     pub fn to_string(&self) -> String {
         match self {
-            Move::Default{ mov, piece_type } => {
+            Move::Default{ mov, piece_type: _pt, captured: _c } => {
                 Self::square_to_string(mov.from) + &Self::square_to_string(mov.to)
             },
             Move::Castling{ side, left } => {
@@ -98,10 +94,10 @@ impl Move {
                     }
                 }
             },
-            Move::EnPassant{ mov, beaten } => {
+            Move::EnPassant{ mov, beaten: _ } => {
                 Self::square_to_string(mov.from) + &Self::square_to_string(mov.to)
             },
-            Move::Promotion{ mov, promote_to } => {
+            Move::Promotion{ mov, promote_to, captured: _c } => {
                 Self::square_to_string(mov.from) + &Self::square_to_string(mov.to) + 
                 if *promote_to == QUEEN { "Q" }
                 else if *promote_to == ROOK { "R" }
@@ -204,9 +200,9 @@ pub fn sort_moves(game: &Game, move_list: &mut Vec<Move>) {
 
     move_list.sort_by_key(|mov| {
         match mov {
-            Move::Default { mov, piece_type } => if (from_square(mov.to) & all_pieces) != 0 { -10 } else { 0 },
+            Move::Default { mov, piece_type: _, captured } => if let None = captured { 0 } else { -10 }, //if (from_square(mov.to) & all_pieces) != 0 { -10 } else { 0 },
             Move::Castling { side: _, left: _ } => 0,
-            Move::Promotion { mov, promote_to } => -15,
+            Move::Promotion { mov: _, promote_to: _, captured: _ } => -15,
             Move::EnPassant { mov: _, beaten: _ } => -10,
         }
     });
@@ -229,7 +225,7 @@ fn generate_pawn_pushes(game: &Game, side: Side, move_list: &mut Vec<Move>) {
             WHITE => south_one(p),
             BLACK => north_one(p)
         };
-        move_list.push(Move::Default { mov: SimpleMove { from: square(origin), to: square(p) }, piece_type: PAWN });
+        move_list.push(Move::Default { mov: SimpleMove { from: square(origin), to: square(p) }, piece_type: PAWN, captured: None });
     }
 }
 
@@ -252,7 +248,7 @@ fn generate_pawn_doublepushes(game: &Game, side: Side, move_list: &mut Vec<Move>
             WHITE => south_one(south_one(p)),
             BLACK => north_one(north_one(p))
         };
-        move_list.push(Move::Default { mov: SimpleMove { from: square(origin), to: square(p) }, piece_type: PAWN });
+        move_list.push(Move::Default { mov: SimpleMove { from: square(origin), to: square(p) }, piece_type: PAWN, captured: None });
     }
 }
 
@@ -275,14 +271,15 @@ fn generate_pawn_captures(game: &Game, side: Side, move_list: &mut Vec<Move>) {
 
         for cap in [left_cap, right_cap].iter() {
             if cap & others != 0 {
-                let simple_move = SimpleMove { from: square(pawn), to: square(*cap) };
+                let captured = game.find_piece(*cap);
+                let simple_move = SimpleMove { from: square(pawn), to: square(*cap)};
                 if cap & promotion_mask != 0 {
-                    move_list.push(Move::Promotion { mov: simple_move, promote_to: QUEEN });
-                    move_list.push(Move::Promotion { mov: simple_move, promote_to: ROOK });
-                    move_list.push(Move::Promotion { mov: simple_move, promote_to: BISHOP });
-                    move_list.push(Move::Promotion { mov: simple_move, promote_to: KNIGHT });
+                    move_list.push(Move::Promotion { mov: simple_move, promote_to: QUEEN, captured: captured });
+                    move_list.push(Move::Promotion { mov: simple_move, promote_to: ROOK, captured: captured });
+                    move_list.push(Move::Promotion { mov: simple_move, promote_to: BISHOP, captured: captured });
+                    move_list.push(Move::Promotion { mov: simple_move, promote_to: KNIGHT, captured: captured });
                 } else {
-                    move_list.push(Move::Default { mov: simple_move, piece_type: PAWN });
+                    move_list.push(Move::Default { mov: simple_move, piece_type: PAWN, captured: captured });
                 }
             }
         }
@@ -308,10 +305,10 @@ fn generate_promotions(game: &Game, side: Side, move_list: &mut Vec<Move>) {
         };
         let movement = SimpleMove { from: square(origin), to: square(p) };
         //move_list.push(Move::Default { mov: SimpleMove { from: square(origin), to: square(p) }, piece_type: PAWN });
-        move_list.push(Move::Promotion { mov: movement, promote_to: QUEEN });
-        move_list.push(Move::Promotion { mov: movement, promote_to: ROOK });
-        move_list.push(Move::Promotion { mov: movement, promote_to: BISHOP });
-        move_list.push(Move::Promotion { mov: movement, promote_to: KNIGHT });
+        move_list.push(Move::Promotion { mov: movement, promote_to: QUEEN, captured: None });
+        move_list.push(Move::Promotion { mov: movement, promote_to: ROOK, captured: None });
+        move_list.push(Move::Promotion { mov: movement, promote_to: BISHOP, captured: None });
+        move_list.push(Move::Promotion { mov: movement, promote_to: KNIGHT, captured: None });
     }
 }
 
@@ -358,15 +355,20 @@ fn generate_knight_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, cap
     let knight_iter = BitboardIterator(knights);
 
     for k in knight_iter {
-        let target_mask = if captures_only {
-            !friends & others
-        } else {
-            !friends
-        };
-        let targets = BitboardIterator(get_knight_targets(square(k)) & target_mask);
-        for target in targets {
+        let cap_targets = BitboardIterator(get_knight_targets(square(k)) & (!friends & others));
+        let nocap_targets = BitboardIterator(get_knight_targets(square(k)) & (!friends & !others));
+
+        for target in cap_targets {
+            let target_square = square(target);
             let simple_move = SimpleMove { from: square(k), to: square(target) };
-            move_list.push(Move::Default{ mov: simple_move, piece_type: KNIGHT });
+            let captured = game.find_piece(target);
+            move_list.push(Move::Default{ mov: simple_move, piece_type: KNIGHT, captured });
+        }
+        if !captures_only {
+            for target in nocap_targets {
+                let simple_move = SimpleMove { from: square(k), to: square(target) };
+                move_list.push(Move::Default{ mov: simple_move, piece_type: KNIGHT, captured: None });
+            }
         }
     }
 }
@@ -394,24 +396,24 @@ fn generate_bishop_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, cap
     let friends = game.get_all_side(side);
     let others = game.get_all_side(!side);
     
-    generate_sliding_moves(friends, others, game.bishops(side), BISHOP, false, true, move_list, captures_only);
+    generate_sliding_moves(game, friends, others, game.bishops(side), BISHOP, false, true, move_list, captures_only);
 }
 
 fn generate_rook_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
     let friends = game.get_all_side(side);
     let others = game.get_all_side(!side);
     
-    generate_sliding_moves(friends, others, game.rooks(side), ROOK, true, false, move_list, captures_only);
+    generate_sliding_moves(game, friends, others, game.rooks(side), ROOK, true, false, move_list, captures_only);
 }
 
 fn generate_queen_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
     let friends = game.get_all_side(side);
     let others = game.get_all_side(!side);
     
-    generate_sliding_moves(friends, others, game.queens(side), QUEEN, true, true, move_list, captures_only);
+    generate_sliding_moves(game, friends, others, game.queens(side), QUEEN, true, true, move_list, captures_only);
 }
 
-fn generate_sliding_moves(friends: Bitboard, others: Bitboard, pieces: Bitboard, piece_type: PieceType,
+fn generate_sliding_moves(game: &Game, friends: Bitboard, others: Bitboard, pieces: Bitboard, piece_type: PieceType,
                           straight: bool, diagonal: bool, move_list: &mut Vec<Move>, captures_only: bool) {
     let pieces_iter = BitboardIterator(pieces);
 
@@ -422,7 +424,12 @@ fn generate_sliding_moves(friends: Bitboard, others: Bitboard, pieces: Bitboard,
         let dest_iter = BitboardIterator(destinations & mask);
         for dest in dest_iter {
             let smove = SimpleMove{ from: square(piece), to: square(dest) };
-            move_list.push(Move::Default { mov: smove, piece_type: piece_type});
+            if dest & others != 0 {
+                move_list.push(Move::Default { mov: smove, piece_type: piece_type, captured: game.find_piece(dest) });
+            }
+            else {
+                move_list.push(Move::Default { mov: smove, piece_type: piece_type, captured: None });
+            }
         }
     }
 }
@@ -495,7 +502,8 @@ fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captu
     let area_iter = BitboardIterator(area);
     for destination in area_iter {
         let simple_move = SimpleMove{ from: square(king), to: square(destination) };
-        move_list.push(Move::Default { mov: simple_move, piece_type: KING });
+        let captured = game.find_piece(destination);
+        move_list.push(Move::Default { mov: simple_move, piece_type: KING, captured });
     }
 }
 
@@ -551,12 +559,12 @@ pub fn is_check(game: &Game, side: Side) -> bool {
     let king_square = square(game.get_piece(KING, side));
     let possible_attacks = generate_possattacking_moves(game, !side);
     for mov in possible_attacks {
-        if let Move::Default{ mov, piece_type: _ } = mov {
+        if let Move::Default{ mov, piece_type: _, captured: _ } = mov {
             if mov.to == king_square {
                 return true;
             }
         }
-        else if let Move::Promotion{ mov, promote_to: _ } = mov {
+        else if let Move::Promotion{ mov, promote_to: _, captured: _ } = mov {
             if mov.to == king_square {
                 return true;
             }

+ 87 - 26
src/search.rs

@@ -25,7 +25,7 @@ pub enum SearchResult {
 /**
  * searches for moves and returns the best move found plus its value
  */
-pub fn search(game: &Game, sc: &mut SearchControl, hash: &mut Cache, depth: i32) -> SearchResult {
+pub fn search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, depth: i32) -> SearchResult {
 
     if depth == 0 {
         return SearchResult::Invalid;
@@ -56,10 +56,25 @@ pub fn search(game: &Game, sc: &mut SearchControl, hash: &mut Cache, depth: i32)
 
     let mut valued_moves: Vec<(Move, PosValue)> = Vec::with_capacity(moves.len());
     let mut cancelled = false;
+
+    let game_before = game.clone();
     for mov in moves {
-        let new_game = apply_move(game, mov);
+        let mut new_game = apply_move(game, mov);
+        let undo = game.apply(mov);
+
+        if *game != new_game {
+            info!("move not correct?");
+            info!("move is {}", mov.to_string());
+            info!("game:\n{}", game.beautiful_print());
+            info!("new_game:\n{}", new_game.beautiful_print());
+            info!("CR game: {:?}", game.castling_rights);
+            info!("CR new_game: {:?}", new_game.castling_rights);
+            info!("CR new_game: {:?}", game.castling_rights == new_game.castling_rights);
+            panic!("unequal games");
+        }
+        //assert_eq!(new_game, *game, );
 
-        let hash_entry = hash.lookup(&new_game);
+        let hash_entry: Option<CacheEntry> = None;//hash.lookup(&new_game);
         if let Some(he) = hash_entry {
             match he.entry_type {
                 EntryType::Value => {
@@ -79,18 +94,20 @@ pub fn search(game: &Game, sc: &mut SearchControl, hash: &mut Cache, depth: i32)
         }
 
         //info!("searching {}", mov.to_string());
-        let (mut val, ret) = negamax(&new_game, sc, hash, -beta, -alpha, depth - 1);
+        let (mut val, ret) = negamax(&mut new_game, sc, hash, -beta, -alpha, depth - 1);
         val = -val;
 
         if ret {
             //return (Move::default(), 0);
             cancelled = true;
+            game.undo_move(undo);
             break;
         }
 
         if val >= MAX_VALUE {
             // mate in 1
             info!("yay mate!");
+            game.undo_move(undo);
             return SearchResult::Mate(mov, 1);
         }
 
@@ -102,8 +119,19 @@ pub fn search(game: &Game, sc: &mut SearchControl, hash: &mut Cache, depth: i32)
         }
 
         valued_moves.push((mov, -val));
+        
+        game.undo_move(undo);
+        if *game != game_before {
+            info!("inequal in search");
+            info!("mov was: {}", mov.to_string());
+            info!("game:\n{}", game.beautiful_print());
+            info!("new_game:\n{}", game_before.beautiful_print());
+            panic!("unequal games");
+        }
     }
 
+
+
     //info!("movvalues: {:?}", valued_moves.iter().map(|mv| mv.0.to_string() + " - " + &mv.1.to_string()).collect::<Vec<String>>());
 
     valued_moves.sort_by_key(|mv| (mv.1 * 1000.0f32) as i64);
@@ -129,7 +157,7 @@ pub fn search(game: &Game, sc: &mut SearchControl, hash: &mut Cache, depth: i32)
     }
 }
 
-fn negamax(game: &Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha: PosValue, beta: PosValue, depth: i32) -> (PosValue, bool) {
+fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha: PosValue, beta: PosValue, depth: i32) -> (PosValue, bool) {
     if depth == 0 {
         return (quiescence_search(game, sc, alpha, beta, 7), false);
         let eval = evaluate(game);
@@ -164,16 +192,27 @@ fn negamax(game: &Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha: Pos
             return (0 as _, false);
         }
     }
+
+    let game_before = game.clone();
     for mov in moves {
-        let new_game = apply_move(game, mov);
-        let (mut val, ret) = negamax(&new_game, sc, hash, -beta, -alpha, depth - 1);
+        let undo = game.apply(mov);
+        let (mut val, ret) = negamax(game, sc, hash, -beta, -alpha, depth - 1);
         val = -val;
 
         if ret {
+            game.undo_move(undo);
             return (alpha, ret)
         }
 
         if val >= beta {
+            game.undo_move(undo);
+            if *game != game_before {
+                info!("move was:\n{}", mov.to_string());
+                info!("unequal in negamax");
+                info!("game:\n{}", game.beautiful_print());
+                info!("new_game:\n{}", game_before.beautiful_print());
+                panic!("unequal games");
+            }
             return (beta, false);
         }
         if val > alpha {
@@ -185,12 +224,22 @@ fn negamax(game: &Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha: Pos
             best = val;
             //best_move = mov;
         }
+        game.undo_move(undo);
+        if *game != game_before {
+            info!("move was:\n{}", mov.to_string());
+            info!("unequal in negamax");
+            info!("game:\n{}", game.beautiful_print());
+            info!("new_game:\n{}", game_before.beautiful_print());
+            panic!("unequal games");
+        }
     }
+
+
     //info!("best alpha {}", best);
     return ((alpha as f64 * 0.99) as _, false);
 }
 
-fn quiescence_search(game: &Game, sc: &mut SearchControl, mut alpha: PosValue, beta: PosValue, depth: i32) -> PosValue {
+fn quiescence_search(game: &mut Game, sc: &mut SearchControl, mut alpha: PosValue, beta: PosValue, depth: i32) -> PosValue {
     let val = evaluate(game);
     sc.nodes += 1;
 
@@ -208,29 +257,39 @@ fn quiescence_search(game: &Game, sc: &mut SearchControl, mut alpha: PosValue, b
     if game.get_piece(KING, game.turn) == 0 { return MIN_VALUE; }
 
     let moves = generate_attacking_moves(game, game.turn);
+
+    let game_before = game.clone();
     for mov in moves {
-        let new_game = apply_move(game, mov);
-        let val = -quiescence_search(&new_game, sc, -beta, -alpha, depth - 1);
+        let undo = game.apply(mov);
+
+        let val = -quiescence_search(game, sc, -beta, -alpha, depth - 1);
         if val >= beta {
+            game.undo_move(undo);
+            if *game != game_before {
+                info!("move was:\n{}", mov.to_string());
+                info!("unequal in quiescence");
+                info!("game:\n{}", game.beautiful_print());
+                info!("new_game:\n{}", game_before.beautiful_print());
+                panic!("unequal games");
+            }
             return beta;
         }
         if val > alpha {
             alpha = val;
         }
+        game.undo_move(undo);
+        if *game != game_before {
+            info!("move was:\n{}", mov.to_string());
+            info!("unequal in quiescence");
+            info!("game:\n{}", game.beautiful_print());
+            info!("new_game:\n{}", game_before.beautiful_print());
+            panic!("unequal games");
+        }
     }
     return alpha;
 }
 
 
-pub fn apply(game: &mut Game, mov: Move) {
-    
-}
-
-
-pub fn undo(game: &mut Game, mov: Move) {
-    
-}
-
 pub fn apply_move(game: &Game, mov: Move) -> Game {
     let mut new_game = game.clone();
 
@@ -238,7 +297,7 @@ pub fn apply_move(game: &Game, mov: Move) -> Game {
     let friends = game.get_all_side(side);
     let others = game.get_all_side(!side);
     match mov {
-        Move::Default { mov, piece_type: pt } => {
+        Move::Default{ mov, piece_type: pt, captured: _ } => {
 
             if pt == KING {
                 // invalidate castling rights
@@ -295,19 +354,21 @@ pub fn apply_move(game: &Game, mov: Move) -> Game {
             let new_rook = if left { rook >> 3 } else { rook << 2 };
 
             new_game.set_piece(KING, side, new_king);
-            new_game.set_piece(ROOK, side, new_rook);
+            *new_game.get_piece_mut(ROOK, side) |= new_rook;
+            *new_game.get_piece_mut(ROOK, side) &= !rook;
         },
         Move::EnPassant { mov, beaten } => {
             let pawns = game.pawns(side);
             if let Some(ep) = game.en_passant {
                 *new_game.get_piece_mut(PAWN, side) &= !from_square(mov.from);
                 *new_game.get_piece_mut(PAWN, side) |= from_square(mov.to);
-                *new_game.get_piece_mut(PAWN, !side) |= from_square(beaten);
+                *new_game.get_piece_mut(PAWN, !side) &= !from_square(beaten);
             }
         },
-        Move::Promotion { mov, promote_to } => {
-            if from_square(mov.to) | others != 0 {
-                new_game.apply_mask(!from_square(mov.to));
+        Move::Promotion { mov, promote_to, captured } => {
+            //if from_square(mov.to) & others != 0 {
+            if let Some(pt) = captured {
+                *new_game.get_piece_mut(pt, !side) &= !from_square(mov.to);
             }
             new_game.apply_mask(!from_square(mov.from));
 
@@ -327,7 +388,7 @@ pub fn apply_move(game: &Game, mov: Move) -> Game {
 
 
     if let Some((ref zt, ref zv)) = new_game.zobrist {
-        info!("applied {}, zobrist now {}", mov.to_string(), *zv);
+        //info!("applied {}, zobrist now {}", mov.to_string(), *zv);
     }
     return new_game;
 }