public void FindMagicMultipliers(ColorlessPiece colorlessPiece, Delegates.WriteMessageLine writeMessageLine = null) { Direction[] directions; ulong[] unoccupiedMoveMasks; ulong[] relevantOccupancyMasks; ulong[] magicMultipliers; int[] shifts; ulong[][] moveMasks; // ReSharper disable SwitchStatementHandlesSomeKnownEnumValuesWithDefault switch (colorlessPiece) { case ColorlessPiece.Bishop: directions = new[] { Direction.NorthEast, Direction.SouthEast, Direction.SouthWest, Direction.NorthWest }; unoccupiedMoveMasks = Board.BishopMoveMasks; relevantOccupancyMasks = _bishopRelevantOccupancyMasks; magicMultipliers = _bishopMagicMultipliers; shifts = _bishopShifts; moveMasks = _bishopMoveMasks; break; case ColorlessPiece.Rook: 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($"{colorlessPiece} piece not supported."); } // ReSharper restore SwitchStatementHandlesSomeKnownEnumValuesWithDefault // Generate moves mask on each square. var occupancyToMovesMask = new Dictionary <ulong, ulong>(); var uniqueMovesMasks = new HashSet <ulong>(); for (var square = Square.A8; square < Square.Illegal; square++) { occupancyToMovesMask.Clear(); uniqueMovesMasks.Clear(); var moveDestinations = unoccupiedMoveMasks[(int)square]; var relevantMoveDestinations = moveDestinations & relevantOccupancyMasks[(int)square]; var uniqueOccupancies = (int)Math.Pow(2, Bitwise.CountSetBits(relevantMoveDestinations)); occupancyToMovesMask.EnsureCapacity(uniqueOccupancies); // Generate moves mask for every permutation of occupancy of the relevant destination squares. var occupancyPermutations = Bitwise.GetAllPermutations(relevantMoveDestinations); for (var occupancyIndex = 0; occupancyIndex < occupancyPermutations.Count; occupancyIndex++) { var occupancy = occupancyPermutations[occupancyIndex]; var movesMask = Board.CreateMoveDestinationsMask(square, occupancy, directions); occupancyToMovesMask.Add(occupancy, movesMask); if (!uniqueMovesMasks.Contains(movesMask)) { uniqueMovesMasks.Add(movesMask); } } 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[(int)square] = shift; var magicMultiplier = magicMultipliers[(int)square]; var knownMagicMultiplier = magicMultiplier == 0 ? null : (ulong?)magicMultiplier; (magicMultipliers[(int)square], moveMasks[(int)square]) = FindMagicMultiplier(occupancyToMovesMask, shift, knownMagicMultiplier); writeMessageLine?.Invoke($"{Board.SquareLocations[(int)square],6} {PieceHelper.GetName(colorlessPiece),6} {shift,5} {occupancyToMovesMask.Count,18} {uniqueMovesMasks.Count,12} {magicMultipliers[(int)square],16:X16}"); } }
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}"); } }