diff --git a/Chess-Challenge/src/API/Board.cs b/Chess-Challenge/src/API/Board.cs
index b85d1c383..fdcdec3da 100644
--- a/Chess-Challenge/src/API/Board.cs
+++ b/Chess-Challenge/src/API/Board.cs
@@ -239,6 +239,12 @@ public bool IsDraw()
///
public bool IsInsufficientMaterial() => Arbiter.InsufficentMaterial(board);
+ ///
+ /// Does the given player has castled?
+ /// (this will only detect castled made during the game moves)
+ ///
+ public bool HasCastled(bool white) => white ? board.currentGameState.hasWhiteCastled : board.currentGameState.hasBlackCastled;
+
///
/// Does the given player still have the right to castle kingside?
/// Note that having the right to castle doesn't necessarily mean castling is legal right now
diff --git a/Chess-Challenge/src/Framework/Chess/Board/Board.cs b/Chess-Challenge/src/Framework/Chess/Board/Board.cs
index 9fd4c1f3b..f92a187e5 100644
--- a/Chess-Challenge/src/Framework/Chess/Board/Board.cs
+++ b/Chess-Challenge/src/Framework/Chess/Board/Board.cs
@@ -138,6 +138,9 @@ public void MakeMove(Move move, bool inSearch = true)
int newCastlingRights = currentGameState.castlingRights;
int newEnPassantFile = 0;
+ bool hasWhiteCastled = currentGameState.hasWhiteCastled || IsWhiteToMove && move.MoveFlag == Move.CastleFlag;
+ bool hasBlackCastled = currentGameState.hasBlackCastled || !IsWhiteToMove && move.MoveFlag == Move.CastleFlag;
+
// Update bitboard of moved piece (pawn promotion is a special case and is corrected later)
MovePiece(movedPiece, startSquare, targetSquare);
@@ -275,7 +278,7 @@ public void MakeMove(Move move, bool inSearch = true)
newFiftyMoveCounter = 0;
}
- GameState newState = new(capturedPieceType, newEnPassantFile, newCastlingRights, newFiftyMoveCounter, newZobristKey);
+ GameState newState = new(capturedPieceType, newEnPassantFile, newCastlingRights, hasWhiteCastled, hasBlackCastled, newFiftyMoveCounter, newZobristKey);
gameStateHistory.Push(newState);
currentGameState = newState;
hasCachedInCheckValue = false;
@@ -400,7 +403,7 @@ public void MakeNullMove()
newZobristKey ^= Zobrist.sideToMove;
newZobristKey ^= Zobrist.enPassantFile[currentGameState.enPassantFile];
- GameState newState = new(PieceHelper.None, 0, currentGameState.castlingRights, currentGameState.fiftyMoveCounter + 1, newZobristKey);
+ GameState newState = new (PieceHelper.None, 0, currentGameState.castlingRights, currentGameState.hasWhiteCastled, currentGameState.hasBlackCastled, currentGameState.fiftyMoveCounter + 1, newZobristKey);
currentGameState = newState;
gameStateHistory.Push(currentGameState);
UpdateSliderBitboards();
@@ -519,9 +522,9 @@ public void LoadPosition(FenUtility.PositionInfo posInfo)
plyCount = (posInfo.moveCount - 1) * 2 + (IsWhiteToMove ? 0 : 1);
// Set game state (note: calculating zobrist key relies on current game state)
- currentGameState = new GameState(PieceHelper.None, posInfo.epFile, castlingRights, posInfo.fiftyMovePlyCount, 0);
+ currentGameState = new GameState(PieceHelper.None, posInfo.epFile, castlingRights, false, false, posInfo.fiftyMovePlyCount, 0);
ulong zobristKey = Zobrist.CalculateZobristKey(this);
- currentGameState = new GameState(PieceHelper.None, posInfo.epFile, castlingRights, posInfo.fiftyMovePlyCount, zobristKey);
+ currentGameState = new GameState(PieceHelper.None, posInfo.epFile, castlingRights, false, false, posInfo.fiftyMovePlyCount, zobristKey);
RepetitionPositionHistory.Push(zobristKey);
diff --git a/Chess-Challenge/src/Framework/Chess/Board/GameState.cs b/Chess-Challenge/src/Framework/Chess/Board/GameState.cs
index 23619d471..8e86bd5f2 100644
--- a/Chess-Challenge/src/Framework/Chess/Board/GameState.cs
+++ b/Chess-Challenge/src/Framework/Chess/Board/GameState.cs
@@ -5,6 +5,8 @@ public readonly struct GameState
public readonly int capturedPieceType;
public readonly int enPassantFile;
public readonly int castlingRights;
+ public readonly bool hasWhiteCastled;
+ public readonly bool hasBlackCastled;
public readonly int fiftyMoveCounter;
public readonly ulong zobristKey;
@@ -13,11 +15,13 @@ public readonly struct GameState
public const int ClearBlackKingsideMask = 0b1011;
public const int ClearBlackQueensideMask = 0b0111;
- public GameState(int capturedPieceType, int enPassantFile, int castlingRights, int fiftyMoveCounter, ulong zobristKey)
+ public GameState(int capturedPieceType, int enPassantFile, int castlingRights, bool hasWhiteCastled, bool hasBlackCastled, int fiftyMoveCounter, ulong zobristKey)
{
this.capturedPieceType = capturedPieceType;
this.enPassantFile = enPassantFile;
this.castlingRights = castlingRights;
+ this.hasWhiteCastled = hasWhiteCastled;
+ this.hasBlackCastled = hasBlackCastled;
this.fiftyMoveCounter = fiftyMoveCounter;
this.zobristKey = zobristKey;
}