예제 #1
0
        public AlgebraicMoveFormatter(string pieceSymbols)
        {
            if (pieceSymbols == null || pieceSymbols.Length < 5 || pieceSymbols.Length > 6)
            {
                // Revert back to PGN.
                pieceSymbols = PgnMoveFormatter.PieceSymbols;
            }

            EnumIndexedArray <Piece, string> pieceSymbolArray = EnumIndexedArray <Piece, string> .New();

            int pieceIndex = 0;

            if (pieceSymbols.Length == 6)
            {
                // Support for an optional pawn piece symbol.
                pieceSymbolArray[Piece.Pawn] = pieceSymbols[pieceIndex++].ToString();
            }

            pieceSymbolArray[Piece.Knight] = pieceSymbols[pieceIndex++].ToString();
            pieceSymbolArray[Piece.Bishop] = pieceSymbols[pieceIndex++].ToString();
            pieceSymbolArray[Piece.Rook]   = pieceSymbols[pieceIndex++].ToString();
            pieceSymbolArray[Piece.Queen]  = pieceSymbols[pieceIndex++].ToString();
            pieceSymbolArray[Piece.King]   = pieceSymbols[pieceIndex++].ToString();

            this.pieceSymbols = pieceSymbolArray;
        }
        public override bool Equals(EnumIndexedArray <T, TEnum> lhs, EnumIndexedArray <T, TEnum> rhs)
        {
            if (lhs == rhs)
            {
                return(true);
            }
            if (lhs == null || rhs == null)
            {
                return(false);
            }
            //These cases should not be possible because of the way these classes are constructed.
            // HOWEVER, the data member is public, so somebody _could_ do something stupid and make
            // data=null, or make lhs.data == rhs.data, even though lhs!=rhs (above check)
            //On the other hand, these are just optimizations, so it won't be an issue if we reomve them anyway,
            // Unless someone does something really dumb like setting .data to null or resizing to an incorrect size,
            // in which case things will crash, but any developer who does this deserves to have it crash painfully...
            //if (lhs.data == rhs.data)
            //    return true;
            //if (lhs.data == null || rhs.data == null)
            //    return false;
            int i = lhs.Length;

            //if (rhs.Length != i)
            //    return false;
            while (--i >= 0)
            {
                if (!elementComparer.Equals(lhs[i], rhs[i]))
                {
                    return(false);
                }
            }
            return(true);
        }
 public override int GetHashCode(EnumIndexedArray <T, TEnum> enumIndexedArray)
 {
     //This doesn't work: for two arrays ar1 and ar2, ar1.GetHashCode() != ar2.GetHashCode() even when ar1[i]==ar2[i] for all i (unless of course they are the exact same array object)
     //return engineArray.GetHashCode();
     //Code taken from comment by Jon Skeet - of course - in http://stackoverflow.com/questions/7244699/gethashcode-on-byte-array
     //31 and 17 are used commonly elsewhere, but maybe because everyone is using Skeet's post.
     //On the other hand, this is really not very critical.
     unchecked
     {
         int hash = 17;
         int i    = enumIndexedArray.Length;
         while (--i >= 0)
         {
             hash = hash * 31 + elementComparer.GetHashCode(enumIndexedArray[i]);
         }
         return(hash);
     }
 }
예제 #4
0
        public static void LoadChessPieceImages()
        {
            var array = EnumIndexedArray <ColoredPiece, Image> .New();

            array[ColoredPiece.BlackPawn]   = LoadChessPieceImage("bp");
            array[ColoredPiece.BlackKnight] = LoadChessPieceImage("bn");
            array[ColoredPiece.BlackBishop] = LoadChessPieceImage("bb");
            array[ColoredPiece.BlackRook]   = LoadChessPieceImage("br");
            array[ColoredPiece.BlackQueen]  = LoadChessPieceImage("bq");
            array[ColoredPiece.BlackKing]   = LoadChessPieceImage("bk");
            array[ColoredPiece.WhitePawn]   = LoadChessPieceImage("wp");
            array[ColoredPiece.WhiteKnight] = LoadChessPieceImage("wn");
            array[ColoredPiece.WhiteBishop] = LoadChessPieceImage("wb");
            array[ColoredPiece.WhiteRook]   = LoadChessPieceImage("wr");
            array[ColoredPiece.WhiteQueen]  = LoadChessPieceImage("wq");
            array[ColoredPiece.WhiteKing]   = LoadChessPieceImage("wk");
            ImageArray = array;
        }
예제 #5
0
        /// <summary>
        /// Returns the standard initial position.
        /// </summary>
        public static Position GetInitialPosition()
        {
            var initialPosition = new Position
            {
                SideToMove           = Color.White,
                colorVectors         = EnumIndexedArray <Color, ulong> .New(),
                pieceVectors         = EnumIndexedArray <Piece, ulong> .New(),
                castlingRightsVector = Constants.CastlingTargetSquares,
            };

            initialPosition.colorVectors[Color.White] = Constants.WhiteInStartPosition;
            initialPosition.colorVectors[Color.Black] = Constants.BlackInStartPosition;

            initialPosition.pieceVectors[Piece.Pawn]   = Constants.PawnsInStartPosition;
            initialPosition.pieceVectors[Piece.Knight] = Constants.KnightsInStartPosition;
            initialPosition.pieceVectors[Piece.Bishop] = Constants.BishopsInStartPosition;
            initialPosition.pieceVectors[Piece.Rook]   = Constants.RooksInStartPosition;
            initialPosition.pieceVectors[Piece.Queen]  = Constants.QueensInStartPosition;
            initialPosition.pieceVectors[Piece.King]   = Constants.KingsInStartPosition;

            Debug.Assert(initialPosition.CheckInvariants());

            return(initialPosition);
        }
예제 #6
0
        public void EnumWithDuplicates()
        {
            var array = EnumIndexedArray <_EnumWithDuplicates, int> .New();

            Assert.Equal(3, array.Length);
        }
예제 #7
0
        public void EmptyEnum()
        {
            var array = EnumIndexedArray <_EmptyEnum, int> .New();

            Assert.Equal(0, array.Length);
        }
예제 #8
0
 private void AssertEnumIsIllegal <T>() where T : struct
 {
     // The beauty of this is that the static constructor is only run when already inside the closure.
     Assert.Throws <TypeInitializationException>(() => EnumIndexedArray <T, int> .New());
 }
 public EnumIndexedArray(EnumIndexedArray <T, TEnum> inputArray)
 {
     Array.Copy(inputArray.data, data, data.Length);
 }
예제 #10
0
        static Constants()
        {
            CastleQueensideRookDelta = EnumIndexedArray <Color, ulong> .New();

            CastleKingsideRookDelta = EnumIndexedArray <Color, ulong> .New();

            FileMasks = EnumIndexedArray <Square, ulong> .New();

            InnerFileMasks = EnumIndexedArray <Square, ulong> .New();

            RankMasks = EnumIndexedArray <Square, ulong> .New();

            InnerRankMasks = EnumIndexedArray <Square, ulong> .New();

            A1H8Masks = EnumIndexedArray <Square, ulong> .New();

            InnerA1H8Masks = EnumIndexedArray <Square, ulong> .New();

            A8H1Masks = EnumIndexedArray <Square, ulong> .New();

            InnerA8H1Masks = EnumIndexedArray <Square, ulong> .New();

            PawnMoves = ColorSquareIndexedArray <ulong> .New();

            PawnCaptures = ColorSquareIndexedArray <ulong> .New();

            EnPassantSquares = ColorSquareIndexedArray <ulong> .New();

            PawnTwoSquaresAhead = ColorSquareIndexedArray <ulong> .New();

            KnightMoves = EnumIndexedArray <Square, ulong> .New();

            Neighbours = EnumIndexedArray <Square, ulong> .New();

            fileMultipliers = EnumIndexedArray <Square, ulong> .New();

            rankShifts = EnumIndexedArray <Square, int> .New();

            a1h8Multipliers = EnumIndexedArray <Square, ulong> .New();

            a8h1Multipliers = EnumIndexedArray <Square, ulong> .New();

            ulong[] allFiles     = { FileA, FileB, FileC, FileD, FileE, FileF, FileG, FileH };
            ulong[] allRanks     = { Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8 };
            ulong[] allDiagsA1H8 = { DiagA8, DiagA7B8, DiagA6C8, DiagA5D8, DiagA4E8, DiagA3F8, DiagA2G8, DiagA1H8, DiagB1H7, DiagC1H6, DiagD1H5, DiagE1H4, DiagF1H3, DiagG1H2, DiagH1 };
            ulong[] allDiagsA8H1 = { DiagA1, DiagA2B1, DiagA3C1, DiagA4D1, DiagA5E1, DiagA6F1, DiagA7G1, DiagA8H1, DiagB8H2, DiagC8H3, DiagD8H4, DiagE8H5, DiagF8H6, DiagG8H7, DiagH8 };

            ulong[] fileMultipliers8 =
            {
                0x8040201008040200, //A
                0x4020100804020100, //B
                0x2010080402010080, //C
                0x1008040201008040, //D
                0x0804020100804020, //E
                0x0402010080402010, //F
                0x0201008040201008, //G
                0x0100804020100804  //H
            };

            ulong[] a1h8Multipliers8 =
            {
                0,                  //A8
                0,                  //A7-B8
                0x0101010101010100, //A6-C8
                0x0101010101010100, //A5-D8
                0x0101010101010100, //A4-E8
                0x0101010101010100, //A3-F8
                0x0101010101010100, //A2-G8
                0x0101010101010100, //A1-H8
                0x8080808080808000, //B1-H7
                0x4040404040400000, //C1-H6
                0x2020202020000000, //D1-H5
                0x1010101000000000, //E1-H4
                0x0808080000000000, //F1-H3
                0,                  //G1-H2
                0                   //H1
            };

            ulong[] a8h1Multipliers8 =
            {
                0,                  //A1
                0,                  //A2-B1
                0x0101010101010100, //A3-C1
                0x0101010101010100, //A4-D1
                0x0101010101010100, //A5-E1
                0x0101010101010100, //A6-F1
                0x0101010101010100, //A7-G1
                0x0101010101010100, //A8-H1
                0x0080808080808080, //B8-H2
                0x0040404040404040, //C8-H3
                0x0020202020202020, //D8-H4
                0x0010101010101010, //E8-H5
                0x0008080808080808, //F8-H6
                0,                  //G8-H7
                0                   //H8
            };

            CastleQueensideRookDelta[Color.White] = A1 | D1;
            CastleQueensideRookDelta[Color.Black] = A8 | D8;
            CastleKingsideRookDelta[Color.White]  = H1 | F1;
            CastleKingsideRookDelta[Color.Black]  = H8 | F8;

            for (Square sq = Square.H8; sq >= Square.A1; --sq)
            {
                ulong sqVector  = sq.ToVector();
                int   x         = sq.X();
                int   y         = sq.Y();
                int   a1h8Index = 7 + x - y;
                int   a8h1Index = x + y;

                FileMasks[sq]      = allFiles[x];
                InnerFileMasks[sq] = FileMasks[sq] & ~Rank1 & ~Rank8;
                RankMasks[sq]      = allRanks[y];
                InnerRankMasks[sq] = RankMasks[sq] & ~FileA & ~FileH;
                A1H8Masks[sq]      = allDiagsA1H8[a1h8Index];
                InnerA1H8Masks[sq] = A1H8Masks[sq] & ~FileA & ~FileH & ~Rank1 & ~Rank8;
                A8H1Masks[sq]      = allDiagsA8H1[a8h1Index];
                InnerA8H1Masks[sq] = A8H1Masks[sq] & ~FileA & ~FileH & ~Rank1 & ~Rank8;

                fileMultipliers[sq] = fileMultipliers8[x];
                rankShifts[sq]      = y * 8 + 1;
                a1h8Multipliers[sq] = a1h8Multipliers8[a1h8Index];
                a8h1Multipliers[sq] = a8h1Multipliers8[a8h1Index];

                PawnTwoSquaresAhead[Color.White, sq] = (sqVector & Rank2).North().North();
                PawnTwoSquaresAhead[Color.Black, sq] = (sqVector & Rank7).South().South();

                PawnMoves[Color.White, sq] = sqVector.North()
                                             | PawnTwoSquaresAhead[Color.White, sq]; // 2 squares ahead allowed from the starting square.
                PawnMoves[Color.Black, sq] = sqVector.South()
                                             | PawnTwoSquaresAhead[Color.Black, sq];

                PawnCaptures[Color.White, sq] = sqVector.North().West()
                                                | sqVector.North().East();
                PawnCaptures[Color.Black, sq] = sqVector.South().West()
                                                | sqVector.South().East();

                EnPassantSquares[Color.White, sq] = (sqVector & Rank2).North();
                EnPassantSquares[Color.Black, sq] = (sqVector & Rank7).South();

                KnightMoves[sq] = sqVector.North().North().West()
                                  | sqVector.North().North().East()
                                  | sqVector.North().West().West()
                                  | sqVector.North().East().East()
                                  | sqVector.South().West().West()
                                  | sqVector.South().East().East()
                                  | sqVector.South().South().West()
                                  | sqVector.South().South().East();

                Neighbours[sq] = sqVector.North().East()
                                 | sqVector.North()
                                 | sqVector.North().West()
                                 | sqVector.East()
                                 | sqVector.West()
                                 | sqVector.South().East()
                                 | sqVector.South()
                                 | sqVector.South().West();
            }

            // Reachability calculation is done in a few stages.
            // Given are a bitfield of occupied squares, a source square for which to calculate sliding moves,
            // and a direction, N-S, W-E, NW-SE or SW-NE.
            // Example: take a position in which a white queen is on C3, a black king on E1, and a white king on G8.
            // We'd like to calculate whether or not the black king is in check, so:
            // source square = E1, direction = NW-SE, occupied square bitfield = 2^4 (E1) + 2^18 (C3) + 2^62 (G8).
            //
            // A) Zero out everything that's not on the file/rank/diagonal to consider.
            //    In the example, only A5, B4, C3, D2, E1 are relevant, so square G8 is zeroed out.
            //    Also ignore the two edge squares, because no further squares can be blocked.
            //    In the example, this means that square E1 is zeroed out as well.
            //    Only a maximum of six bits of information remain, in the example only three (B4, C3, D2).
            // B) Multiply by a 'magic' value to map and rotate the value onto the eighth rank.
            //    This will generate some garbage on the other seven ranks.
            // C) Shift right by 57 positions to move the value to the first rank instead. This also shifts out the garbage.
            //    In the example, the result binary is: 000010
            //    Had D2 been occupied as well, the result would have been: 000110
            //    And had B4 been occupied as well, the result would have been: 000111
            // D) Given the source square and the 6-bit occupancy index, use a lookup table to obtain a bitfield of reachable
            //    squares. The end result of the example then is a bitfield in which bits are set for C3 and D2.
            //    Since there's a queen is on C3, this means that the black king is indeed in check.
            //
            // Note that the color of the piece occupying a square can be safely ignored,
            // because all squares occupied by pieces of the same color can be zeroed out afterwards.
            // The number of necessary operations is always four: zero out, multiply, shift right, lookup in table.

            // The order in a ray is determined by which bit in the occupancy index corresponds to a given blocking square.
            // This in turn is determined indirectly by the multipliers which are used in step B.
            Square[] baseFile = { Square.A8, Square.A7, Square.A6, Square.A5, Square.A4, Square.A3, Square.A2, Square.A1 };
            ulong[,] baseFileReachability = CalculateRayReachabilityTable(baseFile);
            fileReachability = new ulong[TotalSquareCount, totalOccupancies];

            Square[] baseRank = { Square.A1, Square.B1, Square.C1, Square.D1, Square.E1, Square.F1, Square.G1, Square.H1 };
            ulong[,] baseRankReachability = CalculateRayReachabilityTable(baseRank);
            rankReachability = new ulong[TotalSquareCount, totalOccupancies];

            Square[] baseA1H8 = { Square.A1, Square.B2, Square.C3, Square.D4, Square.E5, Square.F6, Square.G7, Square.H8 };
            ulong[,] baseA1H8Reachability = CalculateRayReachabilityTable(baseA1H8);
            a1h8Reachability = new ulong[TotalSquareCount, totalOccupancies];

            Square[] baseA8H1 = { Square.A8, Square.B7, Square.C6, Square.D5, Square.E4, Square.F3, Square.G2, Square.H1 };
            ulong[,] baseA8H1Reachability = CalculateRayReachabilityTable(baseA8H1);
            a8h1Reachability = new ulong[TotalSquareCount, totalOccupancies];

            // To zero out part of a diagonal before shifting it to another shorter diagonal.
            ulong[] partialDiagMasks =
            {
                ulong.MaxValue,
                ulong.MaxValue & ~FileH,
                ulong.MaxValue & ~FileG & ~FileH,
                ulong.MaxValue & ~FileF & ~FileG & ~FileH,
                ulong.MaxValue & ~FileE & ~FileF & ~FileG & ~FileH,
                ulong.MaxValue & ~FileD & ~FileE & ~FileF & ~FileG & ~FileH,
                ulong.MaxValue & ~FileC & ~FileD & ~FileE & ~FileF & ~FileG & ~FileH,
                ulong.MaxValue & ~FileB & ~FileC & ~FileD & ~FileE & ~FileF & ~FileG & ~FileH,
            };

            for (Square sq = Square.H8; sq >= Square.A1; --sq)
            {
                int x = sq.X();
                int y = sq.Y();
                // Diagonal indexes relative to the long diagonals.
                int a1h8Index = x - y;
                int a8h1Index = x + y - 7;

                for (int o = totalOccupancies - 1; o >= 0; --o)
                {
                    // Copy from the base reachability table, and shift the result to the current rank/file/diagonal.
                    fileReachability[(int)sq, o] = baseFileReachability[7 - y, o] << x;
                    rankReachability[(int)sq, o] = baseRankReachability[x, o] << (y * 8);

                    if (a1h8Index == 0)
                    {
                        a1h8Reachability[(int)sq, o] = baseA1H8Reachability[x, o];
                    }
                    else if (a1h8Index < 0)
                    {
                        ulong a1h8Diag = baseA1H8Reachability[x, o] & partialDiagMasks[-a1h8Index];
                        a1h8Reachability[(int)sq, o] = a1h8Diag << (-a1h8Index * 8);
                    }
                    else
                    {
                        ulong a1h8Diag = baseA1H8Reachability[y, o] & partialDiagMasks[a1h8Index];
                        a1h8Reachability[(int)sq, o] = a1h8Diag << a1h8Index;
                    }

                    if (a8h1Index == 0)
                    {
                        a8h1Reachability[(int)sq, o] = baseA8H1Reachability[x, o];
                    }
                    else if (a8h1Index < 0)
                    {
                        ulong a8h1Diag = baseA8H1Reachability[x, o] & partialDiagMasks[-a8h1Index];
                        a8h1Reachability[(int)sq, o] = a8h1Diag >> (-a8h1Index * 8);
                    }
                    else
                    {
                        ulong a8h1Diag = baseA8H1Reachability[7 - y, o] & partialDiagMasks[a8h1Index];
                        a8h1Reachability[(int)sq, o] = a8h1Diag << a8h1Index;
                    }
                }
            }
        }