/// <summary> /// Limitation: castling moves decoded from a string can't be undone /// </summary> public void UndoMove(Move move) { #if DEBUG _moveHistory.Pop(); #endif _colorToMove = 1 - _colorToMove; var state = _gameStateHistory.Pop(); if (state.CapturedPiece != Piece.None) { if (move.IsEnPassant) { var enPassantOffset = WhiteToMove ? 8 : -8; EnemyPieces.Add(state.CapturedPiece, move.EndSquare + enPassantOffset); } else { EnemyPieces.Add(state.CapturedPiece, move.EndSquare); } // Restore original piece position, replacing with pawn in case of promotion move PiecesToMove.Move(move.EndSquare, move.StartSquare, move.PromotionPiece != Piece.None ? Piece.Pawn : Piece.None); } else if (move.IsCastling) { // Return rook and King to it's original square (int rookOriginalSquare, int rookEndSquare) = GetCastlingRookMove(move); PiecesToMove.RemoveAt(rookEndSquare); PiecesToMove.RemoveAt(GetCastlingKingSqare(move)); PiecesToMove.Add(Piece.King, move.StartSquare); PiecesToMove.Add(Piece.Rook, rookOriginalSquare); } else { // Restore original piece position, replacing with pawn in case of promotion move PiecesToMove.Move(move.EndSquare, move.StartSquare, move.PromotionPiece != Piece.None ? Piece.Pawn : Piece.None); } TotalMoves -= _colorToMove; #if MOVE_CONSISTENCY_CHECK var previousPosition = _fenHistory.Pop(); var restoredPosition = _fenWriter.Write(this, true); if (previousPosition != restoredPosition) { System.Diagnostics.Debugger.Break(); } #endif }
public Position MakeMove(Move move) { #if DEBUG _moveHistory.Push(move); #endif #if MOVE_CONSISTENCY_CHECK _fenHistory.Push(_fenWriter.Write(this, true)); #endif // Get the curent state to apply changes on top of it var state = CurrentState; state.HalfMoveClock++; state.CapturedPiece = Piece.None; int newEnPassantSquare = -1; // Adjust castling flags before actually moving pieces Castling enemyCastlingOptions = state[1 - _colorToMove]; Castling castlingOptions = state[_colorToMove]; if (enemyCastlingOptions != 0 && EnemyPieces[move.EndSquare] == Piece.Rook) { // One of the rooks was captured enemyCastlingOptions &= move.EndSquare % Constants.BoardFiles == QueenSideRookFile ? ~Castling.QueenSide : ~Castling.KingSide; } if (castlingOptions != 0) { if (PiecesToMove[move.StartSquare] == Piece.Rook) { // One of the rooks was moved castlingOptions &= move.StartSquare % Constants.BoardFiles == QueenSideRookFile ? ~Castling.QueenSide : ~Castling.KingSide; } else if (PiecesToMove[move.StartSquare] == Piece.King) { // King was moved castlingOptions = 0; } } if (EnemyPieces[move.EndSquare] != Piece.None) { // Capture, remove enemy piece from the board and reset the clock state.CapturedPiece = EnemyPieces[move.EndSquare]; state.HalfMoveClock = 0; EnemyPieces.RemoveAt(move.EndSquare); PiecesToMove.Move(move.StartSquare, move.EndSquare, move.PromotionPiece); } else if (PiecesToMove[move.StartSquare] == Piece.Pawn) { // Reset the clock on pawn moves state.HalfMoveClock = 0; var enPassantOffset = WhiteToMove ? 8 : -8; if (move.EndSquare == state.EnPassantSquare) { // EnPassant, remove enemy pawn from the board state.CapturedPiece = Piece.Pawn; EnemyPieces.RemoveAt(move.EndSquare + enPassantOffset); } else if (move.StartSquare - move.EndSquare == enPassantOffset * 2) { // EnPassant is possible next move newEnPassantSquare = move.EndSquare + enPassantOffset; } PiecesToMove.Move(move.StartSquare, move.EndSquare, move.PromotionPiece); } else if (PiecesToMove[move.StartSquare] == Piece.King && (Math.Abs(move.StartSquare - move.EndSquare) == 2 || PiecesToMove[move.EndSquare] == Piece.Rook)) { // Castling // To support general chess 960 case remove king and rook from the board and put them to the final sqares (int rookStartSquare, int rookEndSquare) = GetCastlingRookMove(move); PiecesToMove.RemoveAt(rookStartSquare); PiecesToMove.RemoveAt(move.StartSquare); PiecesToMove.Add(Piece.King, GetCastlingKingSqare(move)); PiecesToMove.Add(Piece.Rook, rookEndSquare); } else { // General piece move PiecesToMove.Move(move.StartSquare, move.EndSquare, move.PromotionPiece); } state[1 - _colorToMove] = enemyCastlingOptions; state[_colorToMove] = castlingOptions; state.EnPassantSquare = newEnPassantSquare; _gameStateHistory.Push(state); // Increment total moves after the black's move only TotalMoves += _colorToMove; _colorToMove = 1 - _colorToMove; return(this); }