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 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; }
/// <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); }
public void EnumWithDuplicates() { var array = EnumIndexedArray <_EnumWithDuplicates, int> .New(); Assert.Equal(3, array.Length); }
public void EmptyEnum() { var array = EnumIndexedArray <_EmptyEnum, int> .New(); Assert.Equal(0, array.Length); }
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()); }
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; } } } }