/// <summary> /// Reverts last move applied to the board /// </summary> public void RevertLastMove() { MoveInformation lastMove = Moves.Last(); Moves.RemoveAt(Moves.Count() - 1); ChessPiece lastPieceMoved = FindPieceAt(lastMove.End.File, lastMove.End.Rank); lastPieceMoved.Move(lastMove.Start.File, lastMove.Start.Rank); if (lastMove.FirstMove) { lastPieceMoved.Deployed = false; // Reset first move } // Undo captures lastMoveWasCapture = false; if (lastMove.IsCapture) { lastMove.CapturedPiece.Captured = false; lastMoveWasCapture = true; } if (lastMove.IsPromotion) { lastPieceMoved.Demote(); } // Undo a castling move if (lastMove.IsCastle) { // Move the rook back as well ChessPiece rook = lastMove.CastlingRook; PieceFile castleTargetFile = lastMove.End.File; int rookRank = (rook.Color == PieceColor.White) ? 1 : 8; if (castleTargetFile.ToInt() == 7) // g->h { rook.Move(new PieceFile('h'), rookRank); } else if (castleTargetFile.ToInt() == 3) // c->a { rook.Move(new PieceFile('a'), rookRank); } else { // It has to be one of the above if logic is correct throw new IndexOutOfRangeException(); } rook.Deployed = false; } // Reset castling rights ActivePlayerCastlingRights = lastMove.CastlingRights; // Flip players activePlayer = OppositeColor(activePlayer); // Set last FEN currentFEN = lastMove.PreviousFEN; }
/// <summary> /// Find the rect for a given location on the board /// </summary> /// <param name="file">File for the piece</param> /// <param name="rank">Rank for the piece</param> /// <returns>Rectangle for the board location in client coordinates</returns> private Rectangle GetRect(PieceFile file, int rank) { // This simple diagram helps demonstrate the inverted relationship // (not pictured is the overall board offset) // // Black White // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ // 8 | |X| |X| |X| |X| | |X| |X| |X| |X| 1 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ // 7 |X| |X| |X| |X| | |X| |X| |X| |X| | 2 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ // 6 | |X| |X| |X| |X| | |X| |X| |X| |X| 3 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ // 5 |X| |X| |X| |X| | |X| |X| |X| |X| | 4 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ // 4 | |X| |X| |X| |X| | |X| |X| |X| |X| 5 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ // 3 |X| |X| |X| |X| | |X| |X| |X| |X| | 6 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ // 2 | |X| |X| |X| |X| | |X| |X| |X| |X| 7 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ // 1 |X| |X| |X| |X| | |X| |X| |X| |X| | 8 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ // a b c d e f g h h g f e d c b a // White Black int X; int Y; // PieceFile is 1-based, same as the physical board if (data.Orientation == BoardOrientation.WhiteOnBottom) { X = (file.ToInt() - 1) * squareSizeInPixels; Y = (8 - rank) * squareSizeInPixels; } else { X = (8 - file.ToInt()) * squareSizeInPixels; Y = (rank - 1) * squareSizeInPixels; } // Return the calculated rect offset from the overall topLeft location return(new Rectangle(X + topLeft.X, Y + topLeft.Y, squareSizeInPixels, squareSizeInPixels)); }
/// <summary> /// Returns true if the current piece is trying to castle. It must be /// a king that moved in the correct manner (2 squares horizontally) /// </summary> /// <param name="piece">ChessPiece to check</param> /// <param name="targetFile">ChessFile the piece is moving to</param> /// <returns>True if the move is a castling move</returns> private bool IsCastling(ChessPiece piece, PieceFile targetFile) { bool result = false; int deltaSquares = Math.Abs(piece.File.ToInt() - targetFile.ToInt()); if ((piece.Job == PieceClass.King) && (deltaSquares == 2)) { result = true; } return(result); }
/// <summary> /// Checks if a given piece can target a specified square. It is not required /// that the square be occupied by an enemy piece, just potentially reachable /// </summary> /// <param name="board">ChessBoard to check against</param> /// <param name="piece">ChessPiece to check</param> /// <param name="targetFile">file to target</param> /// <param name="targetRank">rank to target</param> /// <returns>true if the piece can target the given square</returns> public static bool CanPieceTargetSquare(ChessBoard board, ChessPiece piece, PieceFile targetFile, int targetRank) { bool result = false; if (piece.Captured == true) { return(false); } BoardSquare targetSquare = new BoardSquare(targetFile, targetRank); List <BoardSquare> moves = new List <BoardSquare>(); switch (piece.Job) { case PieceClass.Pawn: // Can't reuse the normal helper as it requires the space to be occupied // also w/o en-passant and double moves, this can be simpler int pawnTargetRank = (piece.Color == PieceColor.White) ? piece.Rank + 1 : piece.Rank - 1; if (targetRank == pawnTargetRank) { if (((piece.File.ToInt() - 1) == targetFile.ToInt()) || ((piece.File.ToInt() + 1) == targetFile.ToInt())) { result = true; } } break; case PieceClass.Knight: moves = GetLegalMoves_Knight(piece, board); break; case PieceClass.Bishop: moves = GetLegalMoves_Bishop(piece, board); break; case PieceClass.Rook: moves = GetLegalMoves_Rook(piece, board); break; case PieceClass.Queen: moves = GetLegalMoves_Queen(piece, board); break; case PieceClass.King: // don't recurse into the normal call, also alternate method to examine // These are pairs of offsets (-1, 0), (-1, 1),...etc so there are twice // as many of these as squares to check int[] offsets = new int[] { -1, 0, -1, 1, -1, -1, 1, 0, 1, 1, 1, -1, 0, 1, 0, -1 }; for (int index = 0; index < offsets.Length / 2; index++) { int fileOffset = offsets[index * 2]; int rankOffset = offsets[(index * 2) + 1]; // Test the validity of the square offset if (BoardSquare.IsValid(piece.File.ToInt() + fileOffset, piece.Rank + rankOffset)) { BoardSquare testSquare = new BoardSquare(new PieceFile(piece.File.ToInt() + fileOffset), piece.Rank + rankOffset); if (testSquare == targetSquare) { result = true; break; } } } break; default: throw new ArgumentOutOfRangeException(); } // King is special above if (piece.Job != PieceClass.King) { // Check moves for the target square foreach (BoardSquare square in moves) { if (square == targetSquare) { result = true; break; } } } return(result); }