private static void PopulateKingCastlingMoves( ICollection <GameMoveData2> resultMoves, Square sourceSquare, Bitboard target, CastlingOptions allowedCastlingOptions, Bitboard nonEmptySquares, CastlingType castlingType) { var option = castlingType.ToOption(); if ((allowedCastlingOptions & option) == 0) { return; } var info = KingCastlingInfos[GetCastlingTypeArrayIndexInternal(castlingType)]; if (info.KingMove.From != sourceSquare || (info.ExpectedEmptySquares & nonEmptySquares).IsAny || (info.KingMove.To.Bitboard & target).IsNone) { return; } var moveData = new GameMoveData2(info.KingMove, GameMoveFlags.IsKingCastling); resultMoves.Add(moveData); }
public static long GetCastlingHash(CastlingOptions castlingOptions) { var result = 0L; if (castlingOptions.HasFlag(CastlingOptions.WhiteKingSide)) { result ^= GetCastlingHash(CastlingType.WhiteKingSide); } if (castlingOptions.HasFlag(CastlingOptions.WhiteQueenSide)) { result ^= GetCastlingHash(CastlingType.WhiteQueenSide); } if (castlingOptions.HasFlag(CastlingOptions.BlackKingSide)) { result ^= GetCastlingHash(CastlingType.BlackKingSide); } if (castlingOptions.HasFlag(CastlingOptions.BlackQueenSide)) { result ^= GetCastlingHash(CastlingType.BlackQueenSide); } return(result); }
private StandardGamePosition( [NotNull] PiecePosition piecePosition, GameSide activeSide, int fullMoveIndex, CastlingOptions castlingOptions, EnPassantCaptureInfo2?enPassantCaptureInfo, int halfMoveCountBy50MoveRule) : base(piecePosition, activeSide, fullMoveIndex) { if (piecePosition is null) { throw new ArgumentNullException(nameof(piecePosition)); } if (halfMoveCountBy50MoveRule < 0) { throw new ArgumentOutOfRangeException( nameof(halfMoveCountBy50MoveRule), halfMoveCountBy50MoveRule, @"The value cannot be negative."); } CastlingOptions = castlingOptions; EnPassantCaptureInfo = enPassantCaptureInfo; HalfMoveCountBy50MoveRule = halfMoveCountBy50MoveRule; //// TODO [HarinezumiSama] IMPORTANT: This Zobrist key algorithm is different from the one used in the Polyglot opening book (in the en-passant part) ZobristKey = PiecePosition.ZobristKey ^ ZobristHashHelper.GetCastlingHash(CastlingOptions) ^ ZobristHashHelper.GetEnPassantHash( EnPassantCaptureInfo, PiecePosition[ActiveSide.ToPiece(PieceType.Pawn)]) ^ ZobristHashHelper.GetTurnHash(ActiveSide); }
protected void GeneratePotentialKingMoves( [NotNull] ICollection <GameMoveData2> resultMoves, GameSide side, GeneratedMoveTypes moveTypes, Bitboard target, CastlingOptions allowedCastlingOptions) { if (resultMoves is null) { throw new ArgumentNullException(nameof(resultMoves)); } var kingPiece = side.ToPiece(PieceType.King); var kings = PiecePosition[kingPiece]; while (kings.IsAny) { var kingSquareIndex = Bitboard.PopFirstSquareIndex(ref kings); var sourceSquare = new Square(kingSquareIndex); var moves = KingAttacksOrMoves[kingSquareIndex]; var movesOnTarget = moves & target; if (moveTypes.IsAnySet(GeneratedMoveTypes.Capture)) { var enemies = PiecePosition[side.Invert()]; var captures = movesOnTarget & enemies; PopulateSimpleMoves(resultMoves, sourceSquare, captures, GameMoveFlags.IsRegularCapture); } //// ReSharper disable once InvertIf if (moveTypes.IsAnySet(GeneratedMoveTypes.Quiet)) { var emptySquares = PiecePosition[Piece.None]; var nonCaptures = movesOnTarget & emptySquares; PopulateSimpleMoves(resultMoves, sourceSquare, nonCaptures, GameMoveFlags.None); var nonEmptySquares = ~emptySquares; PopulateKingCastlingMoves( resultMoves, sourceSquare, target, allowedCastlingOptions, nonEmptySquares, CastlingSide.KingSide.ToCastlingType(side)); PopulateKingCastlingMoves( resultMoves, sourceSquare, target, allowedCastlingOptions, nonEmptySquares, CastlingSide.QueenSide.ToCastlingType(side)); } } }
public void GenerateKingMoves( [NotNull] ICollection <GameMoveData> resultMoves, GameSide side, CastlingOptions allowedCastlingOptions, Bitboard target) { if (resultMoves is null) { throw new ArgumentNullException(nameof(resultMoves)); } var kingPiece = side.ToPiece(PieceType.King); var king = PiecePosition[kingPiece]; if (king.IsNone) { return; } if (!king.IsExactlyOneSquare()) { throw new ChessPlatformException( $@"There are multiple {kingPiece.GetDescription()} pieces ({king.GetSquareCount()}) on the board."); } var kingSquareIndex = king.FindFirstSquareIndex(); var sourceSquare = new Square(kingSquareIndex); var directTargets = KingAttacksOrMoves[kingSquareIndex] & target; var emptySquares = PiecePosition[Piece.None]; var nonCaptures = directTargets & emptySquares; PopulateSimpleMoves(resultMoves, sourceSquare, nonCaptures, GameMoveFlags.None); var enemies = PiecePosition[side.Invert()]; var captures = directTargets & enemies; PopulateSimpleMoves(resultMoves, sourceSquare, captures, GameMoveFlags.IsRegularCapture); var nonEmptySquares = ~emptySquares; PopulateKingCastlingMoves( resultMoves, sourceSquare, allowedCastlingOptions, nonEmptySquares, CastlingSide.KingSide.ToCastlingType(side)); PopulateKingCastlingMoves( resultMoves, sourceSquare, allowedCastlingOptions, nonEmptySquares, CastlingSide.QueenSide.ToCastlingType(side)); }
protected static void AssertBaseProperties( [NotNull] GameBoard gameBoard, GameSide expectedActiveSide, CastlingOptions expectedCastlingOptions, EnPassantCaptureInfo expectedEnPassantCaptureInfo, int expectedHalfMoveCountBy50MoveRule, int expectedFullMoveIndex, GameState expectedGameState, AutoDrawType autoDrawType = AutoDrawType.None) { Assert.That(gameBoard, Is.Not.Null); Assert.That(gameBoard.ActiveSide, Is.EqualTo(expectedActiveSide)); Assert.That(gameBoard.CastlingOptions, Is.EqualTo(expectedCastlingOptions)); AssertEnPassantCaptureInfo(gameBoard.EnPassantCaptureInfo, expectedEnPassantCaptureInfo); Assert.That(gameBoard.FullMoveIndex, Is.EqualTo(expectedFullMoveIndex)); Assert.That(gameBoard.HalfMoveCountBy50MoveRule, Is.EqualTo(expectedHalfMoveCountBy50MoveRule)); Assert.That(gameBoard.FullMoveCountBy50MoveRule, Is.EqualTo(expectedHalfMoveCountBy50MoveRule / 2)); Assert.That(gameBoard.State, Is.EqualTo(expectedGameState)); Assert.That(gameBoard.GetAutoDrawType(), Is.EqualTo(autoDrawType)); }
public static string GetFenSnippet(this CastlingOptions castlingOptions) { if (castlingOptions == CastlingOptions.None) { return(ChessConstants.NoneCastlingOptionsFenSnippet); } var resultBuilder = new StringBuilder(4); foreach (var option in ChessConstants.FenRelatedCastlingOptions) { //// ReSharper disable once InvertIf if (castlingOptions.IsAnySet(option)) { var fenChar = ChessConstants.CastlingOptionToFenCharMap[option]; resultBuilder.Append(fenChar); } } return(resultBuilder.ToString()); }
/// <summary> /// Pass on both the starting rank and file and the ending, we don't want to do board analysis to determine the start. /// But we always need to do some board analysis, so pass on the board anyway. /// </summary> /// <param name="fullNotation">The position of the characters determine their meaning: /// 1. Starting file, noted by a letter a to f. /// 2. Starting rank, noted by a number 1 to 8. /// 3. An x if a piece was struck, nothing if there was no strike. /// 4. Ending file, noted by a letter a to f. /// 5. Ending rank, noted by a number 1 to 8. /// 6. In case of castling use another move constructor. /// 7. In case the king is checked add a +, if it's mated a #</param> public Move(string fullNotation, Board board) { if (fullNotation.Contains("0-0-0")) { this.HasCastled = board.WhiteToMove ? CastlingOptions.WhiteQueenSide : CastlingOptions.BlackQueenSide; } else if (fullNotation.Contains("0-0")) { this.HasCastled = board.WhiteToMove ? CastlingOptions.WhiteKingSide : CastlingOptions.BlackKingSide; } else { var notationIndex = 0; this.Letter = char.IsUpper(fullNotation[notationIndex]) ? fullNotation[notationIndex++] : ' '; this.StartFile = (byte)(fullNotation[notationIndex++] - Constants.FileLetterOffset); this.StartRank = (byte)(fullNotation[notationIndex++] - Constants.RankNumberOffset - 1); var startField = board.Fields[this.StartFile, this.StartRank]; if (startField.Piece == null) { throw new Exception("Invalid argument, no piece found at given location, file: " + this.StartFile + ", rank: " + this.StartRank + 1); } this.White = startField.Piece.White; if (fullNotation[2] == 'x') { this.Struck = true; notationIndex++; } this.EndFile = (byte)(fullNotation[notationIndex++] - Constants.FileLetterOffset); this.EndRank = (byte)(fullNotation[notationIndex++] - Constants.RankNumberOffset); } }
internal MakeMoveData MakeMove( [NotNull] GameMove move, GameSide movingSide, [CanBeNull] EnPassantCaptureInfo enPassantCaptureInfo, ref CastlingOptions castlingOptions) { if (move is null) { throw new ArgumentNullException(nameof(move)); } var piece = PiecePosition[move.From]; if (piece == Piece.None || piece.GetSide() != movingSide) { throw new ArgumentException($@"Invalid move '{move}' in the position.", nameof(move)); } GameMove castlingRookMove = null; Square? enPassantCapturedPieceSquare = null; var movingSideAllCastlingOptions = ChessHelper.GameSideToCastlingOptionsMap[movingSide]; // Performing checks before actual move! var castlingInfo = CheckCastlingMove(move); var isEnPassantCapture = IsEnPassantCapture(move.From, move.To, enPassantCaptureInfo); var isPawnPromotion = IsPawnPromotion(move.From, move.To); var moveData = MovePieceInternal(move); var capturedPiece = moveData.CapturedPiece; if (isEnPassantCapture) { if (enPassantCaptureInfo is null) { throw ChessPlatformException.CreateInconsistentStateError(); } enPassantCapturedPieceSquare = enPassantCaptureInfo.TargetPieceSquare; capturedPiece = PiecePosition.SetPiece(enPassantCaptureInfo.TargetPieceSquare, Piece.None); if (capturedPiece.GetPieceType() != PieceType.Pawn) { throw ChessPlatformException.CreateInconsistentStateError(); } } else if (isPawnPromotion) { if (move.PromotionResult == PieceType.None) { throw new ChessPlatformException($@"Promoted piece type is not specified ({move})."); } var previousPiece = PiecePosition.SetPiece(move.To, move.PromotionResult.ToPiece(movingSide)); if (previousPiece.GetPieceType() != PieceType.Pawn) { throw ChessPlatformException.CreateInconsistentStateError(); } } else if (castlingInfo != null) { if (!castlingOptions.IsAllSet(castlingInfo.Option)) { throw new ChessPlatformException( $@"The castling {{{move}}} ({castlingInfo.CastlingType.GetName()}) is not allowed."); } castlingRookMove = castlingInfo.RookMove; var rookMoveData = MovePieceInternal(castlingRookMove); if (rookMoveData.CapturedPiece != Piece.None) { throw ChessPlatformException.CreateInconsistentStateError(); } castlingOptions &= ~movingSideAllCastlingOptions; } var movingSideCurrentCastlingOptions = castlingOptions & movingSideAllCastlingOptions; if (movingSideCurrentCastlingOptions != CastlingOptions.None) { switch (piece.GetPieceType()) { case PieceType.King: castlingOptions &= ~movingSideAllCastlingOptions; break; case PieceType.Rook: { var castlingInfoByRook = ChessConstants.AllCastlingInfos.SingleOrDefault(obj => obj.RookMove.From == move.From); if (castlingInfoByRook != null) { castlingOptions &= ~castlingInfoByRook.Option; } } break; } } var oppositeSide = movingSide.Invert(); var oppositeSideAllCastlingOptions = ChessHelper.GameSideToCastlingOptionsMap[oppositeSide]; var oppositeSideCurrentCastlingOptions = castlingOptions & oppositeSideAllCastlingOptions; if (oppositeSideCurrentCastlingOptions != CastlingOptions.None && capturedPiece.GetPieceType() == PieceType.Rook) { var oppositeCastlingInfo = ChessConstants.AllCastlingInfos.SingleOrDefault(obj => obj.RookMove.From == move.To); if (oppositeCastlingInfo != null) { castlingOptions &= ~oppositeCastlingInfo.Option; } } var undoMoveData = new MakeMoveData( move, moveData.MovedPiece, capturedPiece, castlingRookMove, enPassantCapturedPieceSquare); _undoMoveDatas.Push(undoMoveData); PiecePosition.EnsureConsistency(); return(undoMoveData); }
public Square[] GetPotentialMoveSquares( CastlingOptions castlingOptions, [CanBeNull] EnPassantCaptureInfo enPassantCaptureInfo, Square sourceSquare) { var piece = PiecePosition[sourceSquare]; var pieceType = piece.GetPieceType(); if (pieceType == PieceType.None) { throw new ArgumentException( $@"No piece at the source square '{sourceSquare}'.", nameof(sourceSquare)); } var pieceSide = piece.GetSide().EnsureNotNull(); switch (pieceType) { case PieceType.Knight: //// TODO [HarinezumiSama] Use bitboard instead of squares var result = ChessHelper.GetKnightMoveSquares(sourceSquare) .Where(square => PiecePosition[square].GetSide() != pieceSide) .ToArray(); return(result); case PieceType.King: case PieceType.Pawn: throw new InvalidOperationException("MUST NOT go into this branch anymore."); case PieceType.None: throw new ArgumentException( $@"No piece at the source square '{sourceSquare}'.", nameof(sourceSquare)); case PieceType.Bishop: case PieceType.Rook: case PieceType.Queen: // Just go ahead break; default: throw pieceType.CreateEnumValueNotImplementedException(); } var resultList = new List <Square>(); if (pieceType.IsSlidingStraight()) { GetPotentialMoveSquaresByRays( sourceSquare, pieceSide, ChessHelper.StraightRays, ChessHelper.MaxSlidingPieceDistance, true, resultList); } if (pieceType.IsSlidingDiagonally()) { GetPotentialMoveSquaresByRays( sourceSquare, pieceSide, ChessHelper.DiagonalRays, ChessHelper.MaxSlidingPieceDistance, true, resultList); } return(resultList.ToArray()); }