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); } }
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}"); } }