private void GenerateQueenMoves(MoveGeneration MoveGeneration, ulong FromSquareMask, ulong ToSquareMask) { ulong queens; ulong unOrEnemyOccupiedSquares; ulong enemyOccupiedSquares; int attacker; if (WhiteMove) { // White Move queens = WhiteQueens & FromSquareMask; unOrEnemyOccupiedSquares = ~OccupancyWhite; enemyOccupiedSquares = OccupancyBlack; attacker = Piece.WhiteQueen; } else { // Black Move queens = BlackQueens & FromSquareMask; unOrEnemyOccupiedSquares = ~OccupancyBlack; enemyOccupiedSquares = OccupancyWhite; attacker = Piece.BlackQueen; } int fromSquare; while ((fromSquare = Bitwise.FindFirstSetBit(queens)) != Square.Illegal) { var bishopOccupancy = Board.BishopMoveMasks[fromSquare] & Occupancy; var rookOccupancy = Board.RookMoveMasks[fromSquare] & Occupancy; var queenDestinations = MoveGeneration switch { MoveGeneration.AllMoves => (Board.PrecalculatedMoves.GetBishopMovesMask(fromSquare, bishopOccupancy) | Board.PrecalculatedMoves.GetRookMovesMask(fromSquare, rookOccupancy)) & unOrEnemyOccupiedSquares & ToSquareMask, MoveGeneration.OnlyCaptures => (Board.PrecalculatedMoves.GetBishopMovesMask(fromSquare, bishopOccupancy) | Board.PrecalculatedMoves.GetRookMovesMask(fromSquare, rookOccupancy)) & enemyOccupiedSquares & ToSquareMask, MoveGeneration.OnlyNonCaptures => (Board.PrecalculatedMoves.GetBishopMovesMask(fromSquare, bishopOccupancy) | Board.PrecalculatedMoves.GetRookMovesMask(fromSquare, rookOccupancy)) & ~Occupancy & ToSquareMask, _ => throw new Exception($"{MoveGeneration} move generation not supported.") }; int toSquare; while ((toSquare = Bitwise.FindFirstSetBit(queenDestinations)) != Square.Illegal) { var victim = GetPiece(toSquare); var move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); if (victim != Piece.None) { Move.SetCaptureAttacker(ref move, attacker); } Move.SetCaptureVictim(ref move, victim); Move.SetIsQuiet(ref move, victim == Piece.None); Moves[MoveIndex] = move; MoveIndex++; Bitwise.ClearBit(ref queenDestinations, toSquare); } Bitwise.ClearBit(ref queens, fromSquare); } }
private void GenerateKnightMoves(MoveGeneration MoveGeneration, ulong FromSquareMask, ulong ToSquareMask) { ulong knights; ulong unOrEnemyOccupiedSquares; ulong enemyOccupiedSquares; int attacker; if (WhiteMove) { // White Move knights = WhiteKnights & FromSquareMask; unOrEnemyOccupiedSquares = ~OccupancyWhite; enemyOccupiedSquares = OccupancyBlack; attacker = Piece.WhiteKnight; } else { // Black Move knights = BlackKnights & FromSquareMask; unOrEnemyOccupiedSquares = ~OccupancyBlack; enemyOccupiedSquares = OccupancyWhite; attacker = Piece.BlackKnight; } int fromSquare; while ((fromSquare = Bitwise.FindFirstSetBit(knights)) != Square.Illegal) { var knightDestinations = MoveGeneration switch { MoveGeneration.AllMoves => Board.KnightMoveMasks[fromSquare] & unOrEnemyOccupiedSquares & ToSquareMask, MoveGeneration.OnlyCaptures => Board.KnightMoveMasks[fromSquare] & enemyOccupiedSquares & ToSquareMask, MoveGeneration.OnlyNonCaptures => Board.KnightMoveMasks[fromSquare] & ~Occupancy & ToSquareMask, _ => throw new Exception($"{MoveGeneration} move generation not supported.") }; int toSquare; while ((toSquare = Bitwise.FindFirstSetBit(knightDestinations)) != Square.Illegal) { var victim = GetPiece(toSquare); var move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); if (victim != Piece.None) { Move.SetCaptureAttacker(ref move, attacker); } Move.SetCaptureVictim(ref move, victim); Move.SetIsQuiet(ref move, victim == Piece.None); Moves[MoveIndex] = move; MoveIndex++; Bitwise.ClearBit(ref knightDestinations, toSquare); } Bitwise.ClearBit(ref knights, fromSquare); } }
public void FindPotentiallyPinnedPieces() { int kingSquare; ulong pieces; ulong enemyBishopsQueens; ulong enemyRooksQueens; if (WhiteMove) { // White Move kingSquare = Bitwise.FindFirstSetBit(WhiteKing); pieces = OccupancyWhite; enemyBishopsQueens = BlackBishops | BlackQueens; enemyRooksQueens = BlackRooks | BlackQueens; } else { // Black Move kingSquare = Bitwise.FindFirstSetBit(BlackKing); pieces = OccupancyBlack; enemyBishopsQueens = WhiteBishops | WhiteQueens; enemyRooksQueens = WhiteRooks | WhiteQueens; } PotentiallyPinnedPieces = 0; var fileAttackers = Board.FileMasks[Board.Files[kingSquare]] & enemyRooksQueens; if (fileAttackers > 0) { PotentiallyPinnedPieces |= Board.FileMasks[Board.Files[kingSquare]] & pieces; } var rankAttackers = Board.RankMasks[Board.WhiteRanks[kingSquare]] & enemyRooksQueens; if (rankAttackers > 0) { PotentiallyPinnedPieces |= Board.RankMasks[Board.WhiteRanks[kingSquare]] & pieces; } var upDiagonalAttackers = Board.UpDiagonalMasks[Board.UpDiagonals[kingSquare]] & enemyBishopsQueens; if (upDiagonalAttackers > 0) { PotentiallyPinnedPieces |= Board.UpDiagonalMasks[Board.UpDiagonals[kingSquare]] & pieces; } var downDiagonalAttackers = Board.DownDiagonalMasks[Board.DownDiagonals[kingSquare]] & enemyBishopsQueens; if (downDiagonalAttackers > 0) { PotentiallyPinnedPieces |= Board.DownDiagonalMasks[Board.DownDiagonals[kingSquare]] & pieces; } }
public static string ToString(ulong Occupancy) { var stringBuilder = new StringBuilder(); for (var rank = 7; rank >= 0; rank--) { for (var file = 0; file < 8; file++) { var square = Board.GetSquare(file, rank); stringBuilder.Append(Bitwise.IsBitSet(Occupancy, square) ? " 1 " : " . "); } stringBuilder.AppendLine(); } return(stringBuilder.ToString()); }
// Castling Bits // 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 // K|Q|k|q // K = White Castle Kingside // Q = White Castle Queenside // k = Black Castle Kingside // q = Black Castle Queenside static Castling() { // Create bit shifts and masks. _whiteKingsideShift = 3; _whiteKingsideMask = Bitwise.CreateUIntMask(3, 3); _whiteKingsideUnmask = Bitwise.CreateUIntUnmask(3, 3); _whiteQueensideShift = 2; _whiteQueensideMask = Bitwise.CreateUIntMask(2, 2); _whiteQueensideUnmask = Bitwise.CreateUIntUnmask(2, 2); _blackKingsideShift = 1; _blackKingsideMask = Bitwise.CreateUIntMask(1, 1); _blackKingsideUnmask = Bitwise.CreateUIntUnmask(1, 1); _blackQueensideMask = Bitwise.CreateUIntMask(0, 0); _blackQueensideUnmask = Bitwise.CreateUIntUnmask(0, 0); }
// CachedPosition.Data Bits // 6 6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 // 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 // To Horizon |Best From |Best To |BMP |Score |SP |Last Accessed // Best From = Best Move From (one extra bit for illegal square) // Best To = Best Move To (one extra bit for illegal square) // BMP = Best Move Promoted Piece // SP = Score Precision static CachedPositionData() { // Create bit shifts and masks. _toHorizonShift = 58; _toHorizonMask = Bitwise.CreateULongMask(58, 63); _toHorizonUnmask = Bitwise.CreateULongUnmask(58, 63); _bestMoveFromShift = 51; _bestMoveFromMask = Bitwise.CreateULongMask(51, 57); _bestMoveFromUnmask = Bitwise.CreateULongUnmask(51, 57); _bestMoveToShift = 44; _bestMoveToMask = Bitwise.CreateULongMask(44, 50); _bestMoveToUnmask = Bitwise.CreateULongUnmask(44, 50); _bestMovePromotedPieceShift = 40; _bestMovePromotedPieceMask = Bitwise.CreateULongMask(40, 43); _bestMovePromotedPieceUnmask = Bitwise.CreateULongUnmask(40, 43); _scoreShift = 10; _scoreMask = Bitwise.CreateULongMask(10, 39); _scoreUnmask = Bitwise.CreateULongUnmask(10, 39); _scorePrecisionShift = 8; _scorePrecisionMask = Bitwise.CreateULongMask(8, 9); _scorePrecisionUnmask = Bitwise.CreateULongUnmask(8, 9); _lastAccessedMask = Bitwise.CreateULongMask(0, 7); _lastAccessedUnmask = Bitwise.CreateULongUnmask(0, 7); }
public void FindPinnedPieces() { int ownKingSquare; ulong ownPieces; ulong enemyRankFileAttackers; ulong enemyDiagonalAttackers; ulong enemyPieces; if (WhiteMove) { // White Move ownKingSquare = Bitwise.FindFirstSetBit(WhiteKing); ownPieces = OccupancyWhite; enemyRankFileAttackers = BlackRooks | BlackQueens; enemyDiagonalAttackers = BlackBishops | BlackQueens; enemyPieces = OccupancyBlack; } else { // Black Move ownKingSquare = Bitwise.FindFirstSetBit(BlackKing); ownPieces = OccupancyBlack; enemyRankFileAttackers = WhiteRooks | WhiteQueens; enemyDiagonalAttackers = WhiteBishops | WhiteQueens; enemyPieces = OccupancyWhite; } // Find pieces pinned to own king by enemy rank / file attackers. PinnedPieces = 0; int attackerSquare; while ((attackerSquare = Bitwise.FindFirstSetBit(enemyRankFileAttackers)) != Square.Illegal) { var betweenSquares = Board.RankFileBetweenSquares[attackerSquare][ownKingSquare]; if (betweenSquares == 0) { Bitwise.ClearBit(ref enemyRankFileAttackers, attackerSquare); continue; } if ((betweenSquares & enemyPieces) == 0) { // No enemy pieces between enemy attacker and own king. var potentiallyPinnedPieces = betweenSquares & ownPieces; if (Bitwise.CountSetBits(potentiallyPinnedPieces) == 1) { // Exactly one own piece between enemy attacker and own king. // Piece is pinned to own king. PinnedPieces |= potentiallyPinnedPieces; } } Bitwise.ClearBit(ref enemyRankFileAttackers, attackerSquare); } // Find pieces pinned to own king by enemy diagonal attackers. while ((attackerSquare = Bitwise.FindFirstSetBit(enemyDiagonalAttackers)) != Square.Illegal) { var betweenSquares = Board.DiagonalBetweenSquares[attackerSquare][ownKingSquare]; if (betweenSquares == 0) { Bitwise.ClearBit(ref enemyDiagonalAttackers, attackerSquare); continue; } if ((betweenSquares & enemyPieces) == 0) { // No enemy pieces between enemy attacker and own king. var potentiallyPinnedPieces = betweenSquares & ownPieces; if (Bitwise.CountSetBits(potentiallyPinnedPieces) == 1) { // Exactly one own piece between enemy attacker and own king. // Piece is pinned to own king. PinnedPieces |= potentiallyPinnedPieces; } } Bitwise.ClearBit(ref enemyDiagonalAttackers, attackerSquare); } }
private void GenerateKingMoves(MoveGeneration MoveGeneration, ulong FromSquareMask, ulong ToSquareMask) { ulong king; ulong unOrEnemyOccupiedSquares; ulong enemyOccupiedSquares; int attacker; bool castleQueenside; ulong castleQueensideMask; bool castleKingside; ulong castleKingsideMask; if (WhiteMove) { // White Move king = WhiteKing & FromSquareMask; unOrEnemyOccupiedSquares = ~OccupancyWhite; enemyOccupiedSquares = OccupancyBlack; attacker = Piece.WhiteKing; castleQueenside = Engine.Castling.WhiteQueenside(Castling); castleQueensideMask = Board.WhiteCastleQEmptySquaresMask; castleKingside = Engine.Castling.WhiteKingside(Castling); castleKingsideMask = Board.WhiteCastleKEmptySquaresMask; } else { // Black Move king = BlackKing & FromSquareMask; unOrEnemyOccupiedSquares = ~OccupancyBlack; enemyOccupiedSquares = OccupancyWhite; attacker = Piece.BlackKing; castleQueenside = Engine.Castling.BlackQueenside(Castling); castleQueensideMask = Board.BlackCastleQEmptySquaresMask; castleKingside = Engine.Castling.BlackKingside(Castling); castleKingsideMask = Board.BlackCastleKEmptySquaresMask; } ulong move; var fromSquare = Bitwise.FindFirstSetBit(king); if (fromSquare == Square.Illegal) { return; } var kingDestinations = MoveGeneration switch { MoveGeneration.AllMoves => Board.KingMoveMasks[fromSquare] & unOrEnemyOccupiedSquares & ToSquareMask, MoveGeneration.OnlyCaptures => Board.KingMoveMasks[fromSquare] & enemyOccupiedSquares & ToSquareMask, MoveGeneration.OnlyNonCaptures => Board.KingMoveMasks[fromSquare] & ~Occupancy & ToSquareMask, _ => throw new Exception($"{MoveGeneration} move generation not supported.") }; int toSquare; while ((toSquare = Bitwise.FindFirstSetBit(kingDestinations)) != Square.Illegal) { var victim = GetPiece(toSquare); move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); if (victim != Piece.None) { Move.SetCaptureAttacker(ref move, attacker); } Move.SetCaptureVictim(ref move, victim); Move.SetIsKingMove(ref move, true); Move.SetIsQuiet(ref move, victim == Piece.None); Moves[MoveIndex] = move; MoveIndex++; Bitwise.ClearBit(ref kingDestinations, toSquare); } if (MoveGeneration != MoveGeneration.OnlyCaptures) { if (castleQueenside && ((Occupancy & castleQueensideMask) == 0)) { // Castle Queenside if (WhiteMove) { // White Move fromSquare = Square.e1; toSquare = Square.c1; } else { // Black Move fromSquare = Square.e8; toSquare = Square.c8; } if ((Board.SquareMasks[toSquare] & ToSquareMask) > 0) { move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); Move.SetIsCastling(ref move, true); Move.SetIsKingMove(ref move, true); Move.SetIsQuiet(ref move, false); Moves[MoveIndex] = move; MoveIndex++; } } if (castleKingside && ((Occupancy & castleKingsideMask) == 0)) { // Castle Kingside if (WhiteMove) { // White Move fromSquare = Square.e1; toSquare = Square.g1; } else { // Black Move fromSquare = Square.e8; toSquare = Square.g8; } if ((Board.SquareMasks[toSquare] & ToSquareMask) > 0) { move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); Move.SetIsCastling(ref move, true); Move.SetIsKingMove(ref move, true); Move.SetIsQuiet(ref move, false); Moves[MoveIndex] = move; MoveIndex++; } } } }
private void GeneratePawnMoves(MoveGeneration MoveGeneration, ulong FromSquareMask, ulong ToSquareMask) { ulong pawns; ulong[] pawnMoveMasks; ulong[] pawnDoubleMoveMasks; ulong[] pawnAttackMasks; ulong enemyOccupiedSquares; var unoccupiedSquares = ~Occupancy; int[] ranks; int attacker; int queen; int rook; int bishop; int knight; int enPassantVictim; if (WhiteMove) { // White Move pawns = WhitePawns & FromSquareMask; pawnMoveMasks = Board.WhitePawnMoveMasks; pawnDoubleMoveMasks = Board.WhitePawnDoubleMoveMasks; pawnAttackMasks = Board.WhitePawnAttackMasks; enemyOccupiedSquares = OccupancyBlack; ranks = Board.WhiteRanks; attacker = Piece.WhitePawn; queen = Piece.WhiteQueen; rook = Piece.WhiteRook; bishop = Piece.WhiteBishop; knight = Piece.WhiteKnight; enPassantVictim = Piece.BlackPawn; } else { // Black Move pawns = BlackPawns & FromSquareMask; pawnMoveMasks = Board.BlackPawnMoveMasks; pawnDoubleMoveMasks = Board.BlackPawnDoubleMoveMasks; pawnAttackMasks = Board.BlackPawnAttackMasks; enemyOccupiedSquares = OccupancyWhite; ranks = Board.BlackRanks; attacker = Piece.BlackPawn; queen = Piece.BlackQueen; rook = Piece.BlackRook; bishop = Piece.BlackBishop; knight = Piece.BlackKnight; enPassantVictim = Piece.WhitePawn; } int fromSquare; ulong move; if ((EnPassantSquare != Square.Illegal) && ((Board.SquareMasks[EnPassantSquare] & ToSquareMask) > 0) && (MoveGeneration != MoveGeneration.OnlyNonCaptures)) { var enPassantAttackers = Board.EnPassantAttackerMasks[EnPassantSquare] & pawns; while ((fromSquare = Bitwise.FindFirstSetBit(enPassantAttackers)) != Square.Illegal) { // Capture pawn en passant. move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, EnPassantSquare); Move.SetCaptureAttacker(ref move, attacker); Move.SetCaptureVictim(ref move, enPassantVictim); Move.SetIsEnPassantCapture(ref move, true); Move.SetIsPawnMove(ref move, true); Move.SetIsQuiet(ref move, false); Moves[MoveIndex] = move; MoveIndex++; Bitwise.ClearBit(ref enPassantAttackers, fromSquare); } } while ((fromSquare = Bitwise.FindFirstSetBit(pawns)) != Square.Illegal) { ulong pawnDestinations; int toSquare; int toSquareRank; if (MoveGeneration != MoveGeneration.OnlyCaptures) { // Pawns may move forward one square (or two if on initial square) if forward squares are unoccupied. pawnDestinations = pawnMoveMasks[fromSquare] & unoccupiedSquares & ToSquareMask; while ((toSquare = Bitwise.FindFirstSetBit(pawnDestinations)) != Square.Illegal) { var doubleMove = Board.SquareDistances[fromSquare][toSquare] == 2; if (doubleMove && ((Occupancy & pawnDoubleMoveMasks[fromSquare]) > 0)) { // Double move is blocked. Bitwise.ClearBit(ref pawnDestinations, toSquare); continue; } toSquareRank = ranks[toSquare]; if (toSquareRank < 7) { move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); Move.SetIsDoublePawnMove(ref move, doubleMove); Move.SetIsPawnMove(ref move, true); Move.SetIsQuiet(ref move, true); Moves[MoveIndex] = move; MoveIndex++; } else { // Promote pawn to queen. move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); Move.SetPromotedPiece(ref move, queen); Move.SetIsPawnMove(ref move, true); Move.SetIsQuiet(ref move, false); Moves[MoveIndex] = move; MoveIndex++; // Promote pawn to rook. move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); Move.SetPromotedPiece(ref move, rook); Move.SetIsPawnMove(ref move, true); Move.SetIsQuiet(ref move, false); Moves[MoveIndex] = move; MoveIndex++; // Promote pawn to bishop. move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); Move.SetPromotedPiece(ref move, bishop); Move.SetIsPawnMove(ref move, true); Move.SetIsQuiet(ref move, false); Moves[MoveIndex] = move; MoveIndex++; // Promote pawn to knight. move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); Move.SetPromotedPiece(ref move, knight); Move.SetIsPawnMove(ref move, true); Move.SetIsQuiet(ref move, false); Moves[MoveIndex] = move; MoveIndex++; } Bitwise.ClearBit(ref pawnDestinations, toSquare); } } if (MoveGeneration != MoveGeneration.OnlyNonCaptures) { // Pawns may attack diagonally forward one square if occupied by enemy. pawnDestinations = pawnAttackMasks[fromSquare] & enemyOccupiedSquares & ToSquareMask; while ((toSquare = Bitwise.FindFirstSetBit(pawnDestinations)) != Square.Illegal) { toSquareRank = ranks[toSquare]; var victim = GetPiece(toSquare); if (toSquareRank < 7) { move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); Move.SetCaptureAttacker(ref move, attacker); Move.SetCaptureVictim(ref move, victim); Move.SetIsPawnMove(ref move, true); Move.SetIsQuiet(ref move, false); Moves[MoveIndex] = move; MoveIndex++; } else { // Promote pawn to queen. move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); Move.SetPromotedPiece(ref move, queen); Move.SetCaptureAttacker(ref move, attacker); Move.SetCaptureVictim(ref move, victim); Move.SetIsPawnMove(ref move, true); Move.SetIsQuiet(ref move, false); Moves[MoveIndex] = move; MoveIndex++; // Promote pawn to rook. move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); Move.SetPromotedPiece(ref move, rook); Move.SetCaptureAttacker(ref move, attacker); Move.SetCaptureVictim(ref move, victim); Move.SetIsPawnMove(ref move, true); Move.SetIsQuiet(ref move, false); Moves[MoveIndex] = move; MoveIndex++; // Promote pawn to bishop. move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); Move.SetPromotedPiece(ref move, bishop); Move.SetCaptureAttacker(ref move, attacker); Move.SetCaptureVictim(ref move, victim); Move.SetIsPawnMove(ref move, true); Move.SetIsQuiet(ref move, false); Moves[MoveIndex] = move; MoveIndex++; // Promote pawn to knight. move = Move.Null; Move.SetFrom(ref move, fromSquare); Move.SetTo(ref move, toSquare); Move.SetPromotedPiece(ref move, knight); Move.SetCaptureAttacker(ref move, attacker); Move.SetCaptureVictim(ref move, victim); Move.SetIsPawnMove(ref move, true); Move.SetIsQuiet(ref move, false); Moves[MoveIndex] = move; MoveIndex++; } Bitwise.ClearBit(ref pawnDestinations, toSquare); } } Bitwise.ClearBit(ref pawns, fromSquare); } }
// Move Bits // Higher priority moves have higher ulong value. // 6 6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 // 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 // B|CapV |CapA |Promo |Kil|History |!|O|K|E|2|P|C|Q|From |To // 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 // B = Best Move // CapV = Capture Victim // CapA = Capture Attacker (inverted) // Promo = Promoted Piece // Kil = Killer Move // ! = Played // O = Castling // K = King Move // E = En Passant Capture // 2 = Double Pawn Move // P = Pawn Move // C = Check // Q = Quiet (not capture, pawn promotion, castling, or check) // From = From (one extra bit for illegal square) // To = To (one extra bit for illegal square) static Move() { // Create bit masks and shifts. _bestShift = 63; _bestMask = Bitwise.CreateULongMask(63); _bestUnmask = Bitwise.CreateULongUnmask(63); _captureVictimShift = 59; _captureVictimMask = Bitwise.CreateULongMask(59, 62); _captureVictimUnmask = Bitwise.CreateULongUnmask(59, 62); _captureAttackerShift = 55; _captureAttackerMask = Bitwise.CreateULongMask(55, 58); _captureAttackerUnmask = Bitwise.CreateULongUnmask(55, 58); _promotedPieceShift = 51; _promotedPieceMask = Bitwise.CreateULongMask(51, 54); _promotedPieceUnmask = Bitwise.CreateULongUnmask(51, 54); _killerShift = 49; _killerMask = Bitwise.CreateULongMask(49, 50); _killerUnmask = Bitwise.CreateULongUnmask(49, 50); _historyShift = 22; _historyMask = Bitwise.CreateULongMask(22, 48); _historyUnmask = Bitwise.CreateULongUnmask(22, 48); _playedShift = 21; _playedMask = Bitwise.CreateULongMask(21); _playedUnmask = Bitwise.CreateULongUnmask(21); _castlingShift = 20; _castlingMask = Bitwise.CreateULongMask(20); _castlingUnmask = Bitwise.CreateULongUnmask(20); _kingMoveShift = 19; _kingMoveMask = Bitwise.CreateULongMask(19); _kingMoveUnmask = Bitwise.CreateULongUnmask(19); _enPassantShift = 18; _enPassantMask = Bitwise.CreateULongMask(18); _enPassantUnmask = Bitwise.CreateULongUnmask(18); _doublePawnMoveShift = 17; _doublePawnMoveMask = Bitwise.CreateULongMask(17); _doublePawnMoveUnmask = Bitwise.CreateULongUnmask(17); _pawnMoveShift = 16; _pawnMoveMask = Bitwise.CreateULongMask(16); _pawnMoveUnmask = Bitwise.CreateULongUnmask(16); _checkShift = 15; _checkMask = Bitwise.CreateULongMask(15); _checkUnmask = Bitwise.CreateULongUnmask(15); _quietShift = 14; _quietMask = Bitwise.CreateULongMask(14); _quietUnmask = Bitwise.CreateULongUnmask(14); _fromShift = 7; _fromMask = Bitwise.CreateULongMask(7, 13); _fromUnmask = Bitwise.CreateULongUnmask(7, 13); _toMask = Bitwise.CreateULongMask(0, 6); _toUnmask = Bitwise.CreateULongUnmask(0, 6); // Set null move. Null = 0; SetIsBest(ref Null, false); SetCaptureVictim(ref Null, Piece.None); SetCaptureAttacker(ref Null, Piece.None); SetPromotedPiece(ref Null, Piece.None); SetKiller(ref Null, 0); SetHistory(ref Null, 0); SetPlayed(ref Null, false); SetIsCastling(ref Null, false); SetIsKingMove(ref Null, false); SetIsEnPassantCapture(ref Null, false); SetIsDoublePawnMove(ref Null, false); SetIsPawnMove(ref Null, false); SetIsCheck(ref Null, false); SetIsQuiet(ref Null, false); SetFrom(ref Null, Square.Illegal); SetTo(ref Null, Square.Illegal); }
public void FindMagicMultipliers(int Piece, Delegates.WriteMessageLine WriteMessageLine = null) { Direction[] directions; ulong[] unoccupiedMoveMasks; ulong[] relevantOccupancyMasks; ulong[] magicMultipliers; int[] shifts; ulong[][] moveMasks; switch (Piece) { case Engine.Piece.WhiteBishop: case Engine.Piece.BlackBishop: directions = new[] { Direction.NorthEast, Direction.SouthEast, Direction.SouthWest, Direction.NorthWest }; unoccupiedMoveMasks = Board.BishopMoveMasks; relevantOccupancyMasks = _bishopRelevantOccupancyMasks; magicMultipliers = _bishopMagicMultipliers; shifts = _bishopShifts; moveMasks = _bishopMoveMasks; break; case Engine.Piece.WhiteRook: case Engine.Piece.BlackRook: directions = new[] { Direction.North, Direction.East, Direction.South, Direction.West }; unoccupiedMoveMasks = Board.RookMoveMasks; relevantOccupancyMasks = _rookRelevantOccupancyMasks; magicMultipliers = _rookMagicMultipliers; shifts = _rookShifts; moveMasks = _rookMoveMasks; break; default: throw new ArgumentException($"{Piece} piece not supported."); } // Generate moves mask on each square. var occupancyToMovesMask = new Dictionary <ulong, ulong>(); var uniqueMovesMasks = new HashSet <ulong>(); for (var square = 0; square < 64; square++) { occupancyToMovesMask.Clear(); uniqueMovesMasks.Clear(); var moveDestinations = unoccupiedMoveMasks[square]; var relevantMoveDestinations = moveDestinations & relevantOccupancyMasks[square]; var uniqueOccupancies = (int)Math.Pow(2, Bitwise.CountSetBits(relevantMoveDestinations)); occupancyToMovesMask.EnsureCapacity(uniqueOccupancies); // Generate moves mask for every permutation of relevant occupancy bits. using (var occupancyPermutations = Bitwise.GetAllPermutations(relevantMoveDestinations).GetEnumerator()) { while (occupancyPermutations.MoveNext()) { var occupancy = occupancyPermutations.Current; if (!occupancyToMovesMask.ContainsKey(occupancy)) { // Have not yet generated moves for this occupancy mask. var movesMask = Board.CreateMoveDestinationsMask(square, occupancy, directions); occupancyToMovesMask.Add(occupancy, movesMask); if (!uniqueMovesMasks.Contains(movesMask)) { uniqueMovesMasks.Add(movesMask); } } } } // Validate enumerator found all permutations of relevant occupancy bits. Debug.Assert(occupancyToMovesMask.Count == uniqueOccupancies); // Determine bit shift that produces number >= unique occupancies. // A stricter condition is number >= unique moves but this requires more computing time to find magic multipliers. var shift = 64 - (int)Math.Ceiling(Math.Log(uniqueOccupancies, 2d)); shifts[square] = shift; var magicMultiplier = magicMultipliers[square]; if (magicMultiplier == 0) { (magicMultipliers[square], moveMasks[square]) = FindMagicMultiplier(occupancyToMovesMask, shift, null); } else { (magicMultipliers[square], moveMasks[square]) = FindMagicMultiplier(occupancyToMovesMask, shift, magicMultiplier); } WriteMessageLine?.Invoke($"{Board.SquareLocations[square],6} {Engine.Piece.GetName(Piece),6} {shift,5} {occupancyToMovesMask.Count,18} {uniqueMovesMasks.Count,12} {magicMultipliers[square],16:X16}"); } }