private static string GetCastlingAvailabilityFenString(Castling castlingInfo) { var castlingAvailability = string.Empty; if (!castlingInfo.HasFlag(Castling.WhiteKingMoved)) { if (!castlingInfo.HasFlag(Castling.WhiteKingsideRookMoved)) { castlingAvailability += "K"; } if (!castlingInfo.HasFlag(Castling.WhiteQueensideRookMoved)) { castlingAvailability += "Q"; } } if (!castlingInfo.HasFlag(Castling.BlackKingMoved)) { if (!castlingInfo.HasFlag(Castling.BlackKingsideRookMoved)) { castlingAvailability += "k"; } if (!castlingInfo.HasFlag(Castling.BlackQueensideRookMoved)) { castlingAvailability += "q"; } } return(string.IsNullOrEmpty(castlingAvailability) ? "-" : castlingAvailability); }
public override HashSet <IMove> PossibleMoves(Board board) { var position = board.FindPiece(this); var topRightCorner = new Position((byte)Math.Min(Position.MaxRow, position.Row + 1), (byte)Math.Min(Position.MaxColumn, position.Column + 1)); var bottomLeftCorner = new Position((byte)Math.Max(Position.MinRow, position.Row - 1), (byte)Math.Max(Position.MinColumn, position.Column - 1)); var range = Positions.Range(topRightCorner, bottomLeftCorner); range.ExceptWith(board.PiecesInRange(range, Color)); var result = new HashSet <IMove>(range.Select(p => new Move(this, p, board))); void AddCastling(bool isLong) { if (Castling.IsCastling(this, isLong, board)) { result.Add(new Castling(this, isLong, board)); } } AddCastling(false); AddCastling(true); return(result); }
protected void HandleCastling(Player player, bool longCastling) { Castling castling = new Castling(player, longCastling); this.game.AddMove(castling); this.board.PerformMove(castling); }
public void Process(char c, ref IProcess step) { switch (c) { case 'w': case 'b': this.found = true; this.result.ActiveColor = c; break; case ' ': if (!this.found) { this.result.Error = true; step = null; } else { step = new Castling(this.result); } break; default: // Unexpected char. this.result.Error = true; step = null; break; } }
public void PerformMove(Castling move) { int y = move.Player == Player.White ? 0 : 7; BoardPosition kingPosition = new BoardPosition(4, y); Man king = GetManOnPosition(kingPosition); int rookX = move.LongCastling ? 0 : 7; BoardPosition rookPosition = new BoardPosition(rookX, y); Man rook = GetManOnPosition(rookPosition); if (king == null || rook == null) { throw new ConverterException("Man to perform castling is not on position: move = " + move); } SetManOnPosition(kingPosition, null); SetManOnPosition(rookPosition, null); if (move.LongCastling) { SetManOnPosition(new BoardPosition(2, y), king); SetManOnPosition(new BoardPosition(3, y), rook); } else { SetManOnPosition(new BoardPosition(6, y), king); SetManOnPosition(new BoardPosition(5, y), rook); } }
private static string GetRookFile(Castling side, int rook, int king) { return(side switch { Castling.KingSide => king <rook?GetFileChar(rook).ToString() : string.Empty, Castling.QueenSide => king> rook?GetFileChar(rook).ToString() : string.Empty, _ => string.Empty });
private static bool CanCastle(Position position, Castling side) { return(side switch { Castling.KingSide => (position.PieceMap & position.KingSideCastlingMask[position.SideIndex]) == 0 && (attacksMap & position.KingSideCastlingAttackMask[position.SideIndex]) == 0, Castling.QueenSide => (position.PieceMap & position.QueenSideCastlingMask[position.SideIndex]) == 0 && (attacksMap & position.QueenSideCastlingAttackMask[position.SideIndex]) == 0, _ => false });
public bool MovePiece(Cell currentCell, Vector3 mousePosition) { var from = new Vector2Int { x = currentCell.position.x, y = currentCell.position.y }; var availableCells = GameEngine.GetAvailableCells(from); foreach (var position in availableCells) { var targetCell = mBoard.mAllCells[position.x, position.y]; if (RectTransformUtility.RectangleContainsScreenPoint(targetCell.mRectTransform, mousePosition)) { MovePiece(currentCell, targetCell); return(true); } } var piece = GameEngine.GetBoard().mPieces[from.x, from.y]; if (piece.GetType() == typeof(King)) { var shortCastlingMoves = Castling.GetShortMoves(currentCell.position, GameEngine); foreach (var castlingMove in shortCastlingMoves) { var targetCell = mBoard.mAllCells[castlingMove.x, castlingMove.y]; if (RectTransformUtility.RectangleContainsScreenPoint(targetCell.mRectTransform, mousePosition)) { MovePiece(currentCell, targetCell); var rookCell = mBoard.mAllCells[7, currentCell.position.y]; var targetRookCell = mBoard.mAllCells[5, currentCell.position.y]; MovePiece(rookCell, targetRookCell); return(true); } } var longCastlingMoves = Castling.GetLongMoves(currentCell.position, GameEngine); foreach (var castlingMove in longCastlingMoves) { var targetCell = mBoard.mAllCells[castlingMove.x, castlingMove.y]; if (RectTransformUtility.RectangleContainsScreenPoint(targetCell.mRectTransform, mousePosition)) { MovePiece(currentCell, targetCell); var rookCell = mBoard.mAllCells[0, currentCell.position.y]; var targetRookCell = mBoard.mAllCells[3, currentCell.position.y]; MovePiece(rookCell, targetRookCell); return(true); } } } return(false); }
public static ulong RemoveCastlingFlag(ulong hash, Castling currentCastling, Castling castlingChange) { if ((currentCastling & castlingChange) != 0) { return(hash ^ _castlingHashes[BitOperations.BitScan((ulong)castlingChange)]); } return(hash); }
public UndoMove(Cell from, Cell to, Piece promotion, Piece capture, Castling castling, Cell enpassant, int drawClock) { From = from; To = to; Promotion = promotion; Capture = capture; Castling = castling; Enpassant = enpassant; DrawClock = drawClock; }
// *** INITIALIZATION *** // #region SetGameVariables public override void SetGameVariables() { base.SetGameVariables(); Array = "r1nb2bn1r/1rnbqkbnr/pppppppppp/10/10/10/10/PPPPPPPPPP/1RNBQKBNR/R1NB2BN1R"; PromotionTypes = "QRBN"; PawnMultipleMove.Value = "Fast Pawn"; EnPassant = true; Castling.AddChoice("Colossus", "King starting on the second rank slides 1-3 spaces short " + "or 1-4 spaces long to castle with the piece on the b or i file"); Castling.Value = "Colossus"; }
// *** INITIALIZATION *** // #region SetGameVariables public override void SetGameVariables() { base.SetGameVariables(); Array = "#{BlackArray}/8/8/8/8/#{WhiteArray}"; Castling.RemoveChoice("Flexible"); WhiteArmy = new ChoiceVariable(new string[] { "Fabulous FIDEs", "Colorbound Clobberers", "Remarkable Rookies", "Nutty Knights" }); BlackArmy = new ChoiceVariable(new string[] { "Fabulous FIDEs", "Colorbound Clobberers", "Remarkable Rookies", "Nutty Knights" }); PawnDoubleMove = true; EnPassant = true; Castling.AddChoice("CwDA", "Standard castling with the extra exception to prevent color-bound pieces from changing square colors"); Castling.Value = "CwDA"; }
public override void SetGameVariables() { base.SetGameVariables(); Array = "w10w/1crnbqkbnrc1/1pppppppppp1/12/12/12/12/12/12/1PPPPPPPPPP1/1CRNBQKBNRC1/W10W"; PromotionRule.AddChoice("Omega", "Standard promotion except that pawns promote on the 11th rank"); PromotionRule.Value = "Omega"; PromotionTypes = "QRBNCW"; Castling.AddChoice("Omega", "Omega Chess requires custom castling handling because the notations of the board squares is non-standard"); Castling.Value = "Omega"; PawnMultipleMove.Value = "@3(2,3)"; EnPassant = true; }
// *** INITIALIZATION *** // #region SetGameVariables public override void SetGameVariables() { base.SetGameVariables(); Array = "12/12/2rnbqkbnr2/2pppppppp2/12/12/12/12/2PPPPPPPP2/2RNBQKBNR2/12/12"; Castling.AddChoice("ChessOnA12x12Board", "Kings on g3 and g10 slide two squares in either direction to castle with the pieces on the c and j files"); Castling.Value = "ChessOnA12x12Board"; PromotionRule.AddChoice("ChessOnA12x12Board", "Standard promotion except that promote on the 10th rank"); PromotionRule.Value = "Custom"; PromotionTypes = "QRBN"; PawnMultipleMove.Value = "@4(2)"; EnPassant = true; }
public void Activate(Vector2Int gridPoint) { if (getPieceTypeAtGrid(gridPoint) == PieceType.King && (gridPoint == new Vector2Int(4, 0) || gridPoint == new Vector2Int(4, 7))) { Castling goTo = GetComponent <Castling>(); goTo.Activate(); } ActiveSelection(gridPoint, false); Display.instance.selectSimplePieceAtGrid(startGridPoint); }
public void Disactivate() { if (!this.enabled) { return; } Castling goTo = GetComponent <Castling>(); goTo.Disactivate(); DisactiveSelection(); }
public Move Parse(string str) { Move move; if (str[0] == '0') { move = Castling.Parse(str, turn); } else { move = new AtomicMove(str); } return(move); }
public Castling this[int side] { get { return(side == 0 ? WhiteCastling : BlackCastling); } set { if (side == 0) { WhiteCastling = value; } else { BlackCastling = value; } } }
public static string GetCastlingOptionsString(Castling option, bool isChess960, List <int> rooks, int king) { string res = string.Empty; if ((option & Castling.KingSide) != 0) { res += isChess960 ? GetRookFile(Castling.KingSide, rooks.Max(), king) : "k"; } if ((option & Castling.QueenSide) != 0) { res += isChess960 ? GetRookFile(Castling.QueenSide, rooks.Min(), king) : "q"; } return(string.Concat(res.OrderBy(c => c))); }
public void HighLightCells(Cell currentCell) { foreach (var highlightedPosition in GameEngine.GetAvailableCells(currentCell.position)) { HighLightCell(highlightedPosition); } if (currentCell.mCurrentPiece != null && typeof(View.Pieces.King) == currentCell.mCurrentPiece.GetType()) { var moves = Castling.GetMoves(currentCell.position, GameEngine); foreach (var move in moves) { HighLightCell(move); } } }
public void CheckMovies_Short_Castling_Occupied_Field() { //Arrange var chessboard = new ChessboardModel(new ChessMovie()); var pieceFactory = new ChessPieceFactory(); chessboard["e1"].Piece = pieceFactory.Create(ColourOfPiece.White, KindOfPiece.King); chessboard["h1"].Piece = pieceFactory.Create(ColourOfPiece.White, KindOfPiece.Rook); chessboard["a6"].Piece = pieceFactory.Create(ColourOfPiece.Black, KindOfPiece.Bishop); var castling = new Castling(); //Act var receivedMovies = castling.CheckMovies(new Position("e1"), chessboard); //Assert Assert.AreEqual(receivedMovies.Count(), 0); }
public void Move(Point start, Point finish) { if (CanMove(start, finish)) { Piece mover = GameBoard.Board.FromPoint(start).Piece; MakeMovement(start, finish); if (mover != null) { //Manages castling. if (mover.ChessPiece == ChessPiece.Rook) { Castling longCastling = mover.Player == Player.White ? Castling.LongWhite : Castling.LongBlack; Castling shortCastling = mover.Player == Player.White ? Castling.ShortWhite : Castling.ShortBlack; if (start.X == 0) { castling &= ~longCastling; } if (start.X == 7) { castling &= ~shortCastling; } } else if (mover.ChessPiece == ChessPiece.King) { castling &= mover.Player == Player.Black ? Castling.LongWhite | Castling.ShortWhite : Castling.LongBlack | Castling.ShortBlack; } //Manages promotions. if (mover.ChessPiece == ChessPiece.Pawn && finish.Y == 7 || finish.Y == 0) { Promotion(mover, finish); } } } else if (!Castle(Turn, start, finish)) { throw new InvalidChessMoveException(); } ChangeTurn(); }
public GameState(Piece[] board, Piece active, Castling castling, Cell enpassant, int draw, int move) { if (board.Length != 64) { throw new ArgumentException("Board must be 64 squares", nameof(board)); } if (active != Piece.White && active != Piece.Black) { throw new ArgumentException("Must be White or Black", nameof(active)); } Board = new Board((Piece[])board.Clone()); Active = active; Castling = castling; Enpassant = enpassant; DrawClock = draw; Move = move; }
private static List <Move> GetCastlingMoves(int startSquare, Position position, bool onlyCaptures = false, bool capturesAndChecks = false) { List <Move> moves = new List <Move>(); // Castling can also deliver a check Castling castlingOptions = position.SideToMoveCastlingOptions; if (((castlingOptions & Castling.KingSide) != 0) && CanCastle(position, Castling.KingSide)) { int endSquare = position.WhiteToMove ? 62 : 6; int rookSqare = (position.WhiteToMove ? 56 : 0) + position.KingSideRookFile; bool isCheck = (enemyCheckMap[Piece.Rook] & (1ul << rookSqare)) != 0; if (!onlyCaptures || capturesAndChecks && isCheck) { moves.Add(new Move(startSquare, position.IsChess960 ? rookSqare : endSquare) { IsCastling = true, IsCheck = isCheck }); } } if (((castlingOptions & Castling.QueenSide) != 0) && CanCastle(position, Castling.QueenSide)) { int endSquare = position.WhiteToMove ? 58 : 2; int rookSqare = (position.WhiteToMove ? 56 : 0) + position.QueenSideRookFile; bool isCheck = (enemyCheckMap[Piece.Rook] & (1ul << rookSqare)) != 0; if (!onlyCaptures || capturesAndChecks && isCheck) { moves.Add(new Move(startSquare, position.IsChess960 ? rookSqare : endSquare) { IsCastling = true, IsCheck = isCheck }); } } return(moves); }
public void CheckMovies_Short_Castling_Movie_Possible() { //Arrange var chessboard = new ChessboardModel(new ChessMovie()); var pieceFactory = new ChessPieceFactory(); chessboard["e1"].Piece = pieceFactory.Create(ColourOfPiece.White, KindOfPiece.King); chessboard["h1"].Piece = pieceFactory.Create(ColourOfPiece.White, KindOfPiece.Rook); chessboard["a6"].Piece = pieceFactory.Create(ColourOfPiece.White, KindOfPiece.Bishop); var castling = new Castling(); //Act var receivedMovies = castling.CheckMovies(new Position("e1"), chessboard); var expectedMovies = new List <string>() { "g1" }; var commonMovies = receivedMovies.Intersect(expectedMovies); //Assert Assert.AreEqual(expectedMovies.Count(), commonMovies.Count()); }
/// <summary> /// 生成文字描述。 /// </summary> public void GenerateFriendlyText() { var sb = new System.Text.StringBuilder(); sb.Append(Piece.GetFriendlyName()); sb.Append("从"); sb.Append(OriginalPosition.ToString()); sb.Append("到"); sb.Append(NewPosition.ToString()); if (IsCapture && CapturedPiece != null) { sb.Append(", 吃"); sb.Append(CapturedPiece.GetFriendlyName()); } if (Promotion.HasValue) { sb.Append(", 晋升为"); sb.Append(ChessGame.OriginalMapPgnCharToPiece(Promotion.Value, Player).GetFriendlyName()); } if (Castling.Equals(CastlingType.KingSide)) { sb.Append(", 王翼易位"); } if (Castling.Equals(CastlingType.QueenSide)) { sb.Append(", 后翼易位"); } if(IsEnpassant) { sb.Append(", 吃过路兵"); } if (IsCheckmate.HasValue && IsCheckmate.Value) { sb.Append(", 将死"); } else if (IsChecking.HasValue && IsChecking.Value) { sb.Append(", 将军"); } this._storedFriendlyText = sb.ToString(); }
// create a board with a FEN string public Board(string fen) { Match Match = FenParser.Match(fen); if (!Match.Success) { throw new ArgumentException(string.Format("invalid FEN string \"{0}\"", fen)); } // piece placement string Field = Match.Groups[1].Value; string[] RankPlacement = Field.Split('/'); BoardAddress Location; int File; Pieces = new PiecePositions(); for (int rank = 0; rank < 8; rank++) { File = 0; foreach (char c in RankPlacement[7 - rank]) { Location = new BoardAddress((char)('a' + File), rank); switch (c) { case 'p': Pieces.Black.Pawns |= Location.Position; break; case 'P': Pieces.White.Pawns |= Location.Position; break; case 'n': Pieces.Black.Knights |= Location.Position; break; case 'N': Pieces.White.Knights |= Location.Position; break; case 'b': Pieces.Black.Bishops |= Location.Position; break; case 'B': Pieces.White.Bishops |= Location.Position; break; case 'r': Pieces.Black.Rooks |= Location.Position; break; case 'R': Pieces.White.Rooks |= Location.Position; break; case 'q': Pieces.Black.Queens |= Location.Position; break; case 'Q': Pieces.White.Queens |= Location.Position; break; case 'k': Pieces.Black.King |= Location.Position; break; case 'K': Pieces.White.King |= Location.Position; break; default: // num empty spaces if (!char.IsDigit(c)) { throw new ArgumentException(string.Format("invalid character \"{0}\" in piece placement field", c)); } File += c - '1'; // convert char to int break; } File++; } } // active color Field = Match.Groups[2].Value.ToLower(); if (Field == "w") { ActiveColorWhite = true; } else { ActiveColorWhite = false; } // castling availablity CastlingAvailability = new Castling(); Field = Match.Groups[3].Value; if (Field == "-") // not available { CastlingAvailability.White = Castling.Move.Disallowed; CastlingAvailability.Black = Castling.Move.Disallowed; } else // available to at least one player // check white { if (Field.Contains("K") && Field.Contains("Q")) { CastlingAvailability.White = Castling.Move.BothSides; } else if (Field.Contains("K")) { CastlingAvailability.White = Castling.Move.KingSide; } else if (Field.Contains("Q")) { CastlingAvailability.White = Castling.Move.QueenSide; } else { CastlingAvailability.White = Castling.Move.Disallowed; } // check black if (Field.Contains("k") && Field.Contains("q")) { CastlingAvailability.Black = Castling.Move.BothSides; } else if (Field.Contains("k")) { CastlingAvailability.Black = Castling.Move.KingSide; } else if (Field.Contains("q")) { CastlingAvailability.Black = Castling.Move.QueenSide; } else { CastlingAvailability.Black = Castling.Move.Disallowed; } } // enpassant location Field = Match.Groups[4].Value; if (Field == "-") { EnPassantTarget = 0; } else { EnPassantTarget = new BoardAddress(Field).Position; } // halfmove clock Field = Match.Groups[5].Value; if (!byte.TryParse(Field, out HalfMoveClock)) { throw new ArgumentException(string.Format("could not parse halfmove clock \"{0}\"", Field)); } // halfmove clock Field = Match.Groups[6].Value; if (!int.TryParse(Field, out FullMoveNumber)) { throw new ArgumentException(string.Format("could not parse full move number \"{0}\"", Field)); } }
/// <summary> /// 生成 SAN 字符串。 /// </summary> /// <param name="gameBeforeTheMove"></param> /// <returns></returns> public string GenerateSANString(ChessGame gameBeforeTheMove) { string SANResult; if (Castling.Equals(CastlingType.KingSide)) { SANResult = "O-O"; } else if (Castling.Equals(CastlingType.QueenSide)) { SANResult = "O-O-O"; } else { var sb = new System.Text.StringBuilder(); if (!(Piece is Pieces.Pawn)) { sb.Append(char.ToUpper(Piece.GetFenCharacter())); } Piece[][] board = gameBeforeTheMove.GetBoard(); List<Move> validMoves = new List<Move>(); for (int r = 0; r < 8; r++) { for (int f = 0; f < 8; f++) { if (board[r][f] != Piece) continue; Move m = new Move(new Position((File)f, 8 - r), this.NewPosition, this.Player, this.Promotion); if (gameBeforeTheMove.IsValidMove(m)) { validMoves.Add(m); } } } if (validMoves.Count == 0) throw new PgnException("This move " + this.ToString() + " is not valid for gameBeforeTheMove."); else if (validMoves.Count > 1) { bool fileUnique = true; bool rankUnique = true; foreach (var move in validMoves) { if(!(move.OriginalPosition.Equals(this.OriginalPosition))) { if (move.OriginalPosition.File == this.OriginalPosition.File) { fileUnique = false; } if (move.OriginalPosition.Rank == this.OriginalPosition.Rank) { rankUnique = false; } } } if (fileUnique) sb.Append((char)((int)'a' + (int)this.OriginalPosition.File)); else if (rankUnique) sb.Append(this.OriginalPosition.Rank.ToString()); else { sb.Append((char)((int)'a' + (int)this.OriginalPosition.File)); sb.Append(this.OriginalPosition.Rank.ToString()); } } if (IsCapture) sb.Append("x"); sb.Append(this.NewPosition.ToString().ToLower()); if (Promotion.HasValue) { sb.Append("="); sb.Append(Promotion.Value); } if (IsCheckmate.HasValue && IsCheckmate.Value) { sb.Append("#"); } else if (IsChecking.HasValue && IsChecking.Value) { sb.Append("+"); } SANResult = sb.ToString(); } try { ChessDotNet.PgnMoveReader.ParseMove(SANResult, Player, gameBeforeTheMove); } catch (PgnException) { throw new System.ArgumentException("This move " + SANResult + " is not valid for gameBeforeTheMove."); } catch (System.ArgumentException) { throw new System.ArgumentException("This move " + SANResult + " is not valid for gameBeforeTheMove."); } this._storedSANString = SANResult; return SANResult; }
public static string ConvertToFEN(IChessItemModel[,] ch_on_b, ChessSide currentSide, Castling whitePossibleCastling, Castling blackPossibleCastling, string captureOnTheIsle, int numberOfReversibleSemiSteps, int moveCount) { StringBuilder sb = new StringBuilder(); //Figure placement string figurePlacement = ConvertToShortFEN(ch_on_b); sb.Append(figurePlacement.Split(' ')[0]); //Current side (black/white) sb.Append(' '); sb.Append(currentSide == ChessSide.black ? "b" : "w"); //Castling possibility sb.Append(' '); bool isWhiteCastlingDone = false; switch (whitePossibleCastling) { case Castling._none: isWhiteCastlingDone = true; break; case Castling._short: sb.Append('K'); break; case Castling._long: sb.Append('Q'); break; case Castling._short_and_long: sb.Append("KQ"); break; case Castling._done: isWhiteCastlingDone = true; break; } switch (blackPossibleCastling) { case Castling._none: if (isWhiteCastlingDone) { sb.Append('-'); } break; case Castling._short: sb.Append('k'); break; case Castling._long: sb.Append('q'); break; case Castling._short_and_long: sb.Append("kq"); break; case Castling._done: if (isWhiteCastlingDone) { sb.Append('-'); } break; } //Capture on the isle sb.Append(' '); sb.Append(captureOnTheIsle); sb.Append(' '); sb.Append(numberOfReversibleSemiSteps); sb.Append(' '); sb.Append(moveCount); return(sb.ToString()); }
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); }