public static GameState ApplyMove(GameState state, ulong startSq, ulong endSq) { var start = BitTranslator.TranslateToSquare(startSq); var end = BitTranslator.TranslateToSquare(endSq); var move = new Move(start.File, start.Rank, end.File, end.Rank); return(ApplyMove(state, move, startSq, endSq)); }
private static bool TryInferStartSquare(BoardState state, ulong piecesOnCurrentSide, bool isWhiteMove, bool isCapture, char pieceDesignation, ref ParsedMoveState result) { var endBit = BitTranslator.TranslateToBit(result.EndFile, result.EndRank); var disambiguityMask = BuildDisambiguityMask(result); switch (pieceDesignation) { case Constants.PieceNotation.King: { var possibleStartBits = MoveGenerator.GetKingMovements(endBit); var existingOfPiece = state.Kings & piecesOnCurrentSide; var actualStartPiece = possibleStartBits & existingOfPiece; if (disambiguityMask != 0) { actualStartPiece &= disambiguityMask; } if (actualStartPiece == 0) { return(false); } var startSquare = BitTranslator.TranslateToSquare(actualStartPiece); result.StartFile = startSquare.File; result.StartRank = startSquare.Rank; return(true); } case Constants.PieceNotation.Knight: { var possibleStartBits = MoveGenerator.GetKnightMovements(endBit); var existingOfPiece = state.Knights & piecesOnCurrentSide; var actualStartPiece = possibleStartBits & existingOfPiece; if (disambiguityMask != 0) { actualStartPiece &= disambiguityMask; } if (actualStartPiece == 0) { return(false); } var startSquare = BitTranslator.TranslateToSquare(actualStartPiece); result.StartFile = startSquare.File; result.StartRank = startSquare.Rank; return(true); } case Constants.PieceNotation.Queen: { var possibleStartBits = MoveGenerator.GetQueenMovements(endBit, state); var existingOfPiece = state.Queens & piecesOnCurrentSide; var actualStartPiece = possibleStartBits & existingOfPiece; if (disambiguityMask != 0) { actualStartPiece &= disambiguityMask; } if (actualStartPiece == 0) { return(false); } var startSquare = BitTranslator.TranslateToSquare(actualStartPiece); result.StartFile = startSquare.File; result.StartRank = startSquare.Rank; return(true); } case Constants.PieceNotation.Rook: { var possibleStartBits = MoveGenerator.GetRookMovements(endBit, state); var existingOfPiece = state.Rooks & piecesOnCurrentSide; var actualStartPiece = possibleStartBits & existingOfPiece; if (disambiguityMask != 0) { actualStartPiece &= disambiguityMask; } if (actualStartPiece == 0) { return(false); } var startSquare = BitTranslator.TranslateToSquare(actualStartPiece); result.StartFile = startSquare.File; result.StartRank = startSquare.Rank; return(true); } case Constants.PieceNotation.Bishop: { var possibleStartBits = MoveGenerator.GetBishopMovements(endBit, state); var existingOfPiece = state.Bishops & piecesOnCurrentSide; var actualStartPiece = possibleStartBits & existingOfPiece; if (disambiguityMask != 0) { actualStartPiece &= disambiguityMask; } if (actualStartPiece == 0) { return(false); } var startSquare = BitTranslator.TranslateToSquare(actualStartPiece); result.StartFile = startSquare.File; result.StartRank = startSquare.Rank; return(true); } case Constants.PieceNotation.Pawn: { ulong actualStartPiece; if (isWhiteMove) { var possibleMoves = endBit >> 8 | endBit >> 16; var possibleAttacks = (endBit >> 7 & (~MoveGenerator.Rank8)) | (endBit >> 9 & (~MoveGenerator.Rank1)); var possibleStart = isCapture ? possibleAttacks : possibleMoves; var pawns = state.Pawns & state.WhitePieces; actualStartPiece = pawns & possibleStart; } else { var possibleMoves = endBit << 8 | endBit << 16; var possibleAttacks = (endBit << 7 & (~MoveGenerator.Rank1)) | (endBit << 9 & (~MoveGenerator.Rank8)); var possibleStart = isCapture ? possibleAttacks : possibleMoves; var pawns = state.Pawns & state.BlackPieces; actualStartPiece = pawns & possibleStart; } if (disambiguityMask != 0) { actualStartPiece &= disambiguityMask; } if (actualStartPiece == 0) { return(false); } var startSquare = BitTranslator.TranslateToSquare(actualStartPiece); result.StartFile = startSquare.File; result.StartRank = startSquare.Rank; return(true); } default: return(false); } }
public static string ToMoveString(Move move, BoardState board, AttackState attackState) { // Algo: Move from end, adding tokens in order // - Check if move is promotion, add tokens if so // - Add end square // - Check if move is capture, output 'x' if so // - Then check if multiple pieces of same type can move to end square, then output first rank or file of moved piece // - Then output piece notation if piece is not a pawn // // - Now that the move string is built, splice to only used tokens var startSquareBit = BitTranslator.TranslateToBit(move.StartFile, move.StartRank); var endSquareBit = BitTranslator.TranslateToBit(move.EndFile, move.EndRank); var movedPiece = board.GetSquareContents(startSquareBit) & ~SquareContents.Colours; Span <char> buffer = stackalloc char[8]; var lastIdx = buffer.Length - 1; if (attackState != AttackState.None) { // Check if move ended in attack var notation = GetAttackStateMoveNotation(attackState); if (notation != 0) { buffer[lastIdx--] = notation; } } if (movedPiece == SquareContents.King) { // Check for castling var squareDiff = (decimal)startSquareBit / endSquareBit; var castleNotation = ""; if (squareDiff == 4) // King has moved 2 squares horizontally towards queenside { castleNotation = "0-0-0"; } else if (squareDiff == 0.25M) // King has moved 2 squares horizontally towards kingside { castleNotation = "0-0"; } if (!string.IsNullOrEmpty(castleNotation)) { var copyIdx = lastIdx - castleNotation.Length + 1; var notationBuffer = buffer.Slice(copyIdx, castleNotation.Length); castleNotation.AsSpan().CopyTo(notationBuffer); lastIdx -= castleNotation.Length; var copyLocation = buffer.Slice(lastIdx + 1); return(copyLocation.ToString()); } } if (move.PromotedPiece != SquareContents.Empty) { var pieceType = move.PromotedPiece & ~SquareContents.Colours; buffer[lastIdx--] = PiecesAsLetters[pieceType]; buffer[lastIdx--] = '='; } buffer[lastIdx--] = (char)(move.EndRank + '0'); buffer[lastIdx--] = move.EndFile; var isCapture = (endSquareBit & board.AllPieces) != 0; if (!isCapture && movedPiece == SquareContents.Pawn) { // Capture pattern for pawn is diagonal movement // Explicitly checking will account for capture by en passant where end square is empty var diff = Math.Max(startSquareBit, endSquareBit) / Math.Min(startSquareBit, endSquareBit); isCapture = diff == 1 << 7 || diff == 1 << 9; } if (isCapture) { buffer[lastIdx--] = 'x'; } if (movedPiece == SquareContents.Pawn) { if (isCapture) { buffer[lastIdx--] = move.StartFile; } } else { if (movedPiece != SquareContents.King) { // Check for disambiguation var colorMask = (startSquareBit & board.WhitePieces) != 0 ? board.WhitePieces : board.BlackPieces; var pieceMask = 0UL; if (movedPiece == SquareContents.Knight) { pieceMask = board.Knights; } else if (movedPiece == SquareContents.Bishop) { pieceMask = board.Bishops; } else if (movedPiece == SquareContents.Rook) { pieceMask = board.Rooks; } else if (movedPiece == SquareContents.Queen) { pieceMask = board.Queens; } pieceMask = pieceMask & colorMask & ~startSquareBit; var otherPossibleStartSquares = new List <Square>(); for (var i = 0; i < 64; i++) { var mask = pieceMask & (1UL << i); if (mask != 0) { var moves = MoveGenerator.GenerateStatelessMovesForPiece(board, mask); if ((moves & endSquareBit) != 0) { otherPossibleStartSquares.Add(BitTranslator.TranslateToSquare(mask)); } } } // There may be multiple possible start squares to disambiguate from var disambiguateByRank = false; var disambiguateByFile = false; foreach (var otherSquare in otherPossibleStartSquares) { disambiguateByRank = disambiguateByRank || (otherSquare.File == move.StartFile); disambiguateByFile = disambiguateByFile || (otherSquare.Rank == move.StartRank); } // Need to disambiguate, but both rank + file are different. Default to by file. if (!disambiguateByFile && !disambiguateByRank && otherPossibleStartSquares.Count > 0) { disambiguateByFile = true; } if (disambiguateByRank) { buffer[lastIdx--] = (char)(move.StartRank + '0'); } if (disambiguateByFile) { buffer[lastIdx--] = move.StartFile; } } buffer[lastIdx--] = PiecesAsLetters[movedPiece]; } var segment = buffer.Slice(lastIdx + 1); return(segment.ToString()); }