/// <summary> /// Populates the given array with the legal moves for the position and /// returns the number of legal moves. /// </summary> /// <param name="moves">The array to populate with the legal moves.</param> /// <returns>The number of legal moves for the position.</returns> public Int32 LegalMoves(Int32[] moves) { // Initialize bitboards and squares that describe the position. Int32 enemy = 1 - SideToMove; Int32 kingSquare = Bit.Read(Bitboard[SideToMove | Piece.King]); UInt64 selfBitboard = Bitboard[SideToMove]; UInt64 enemyBitboard = Bitboard[enemy]; UInt64 targetBitboard = ~selfBitboard; UInt64 enemyBishopQueenBitboard = Bitboard[enemy | Piece.Bishop] | Bitboard[enemy | Piece.Queen]; UInt64 enemyRookQueenBitboard = Bitboard[enemy | Piece.Rook] | Bitboard[enemy | Piece.Queen]; // Initialize variables for move generation. UInt64 checkingBitboard = 0; UInt64 pinningBitboard = 0; Int32 index = 0; // Consider knight and pawn checks. checkingBitboard |= Bitboard[enemy | Piece.Knight] & Attack.Knight(kingSquare); checkingBitboard |= Bitboard[enemy | Piece.Pawn] & Attack.Pawn(kingSquare, SideToMove); // Consider bishop and queen checks and pins. if ((enemyBishopQueenBitboard & Bit.Diagonals[kingSquare]) != 0) { checkingBitboard |= enemyBishopQueenBitboard & Attack.Bishop(kingSquare, OccupiedBitboard); // Determine pinning pieces by removing the first line of defence around the // the king, then seeing which pieces are able to attack. UInt64 defenceRemovedBitboard = OccupiedBitboard; UInt64 defenceBitboard = Bit.RayNE[kingSquare] & selfBitboard; if (defenceBitboard != 0) { defenceRemovedBitboard ^= Bit.IsolateReverse(defenceBitboard); } defenceBitboard = Bit.RayNW[kingSquare] & selfBitboard; if (defenceBitboard != 0) { defenceRemovedBitboard ^= Bit.IsolateReverse(defenceBitboard); } defenceBitboard = Bit.RaySE[kingSquare] & selfBitboard; if (defenceBitboard != 0) { defenceRemovedBitboard ^= Bit.Isolate(defenceBitboard); } defenceBitboard = Bit.RaySW[kingSquare] & selfBitboard; if (defenceBitboard != 0) { defenceRemovedBitboard ^= Bit.Isolate(defenceBitboard); } if (defenceRemovedBitboard != OccupiedBitboard) { pinningBitboard |= enemyBishopQueenBitboard & Attack.Bishop(kingSquare, defenceRemovedBitboard); } } // Consider rook and queen checks and pins. if ((enemyRookQueenBitboard & Bit.Axes[kingSquare]) != 0) { checkingBitboard |= enemyRookQueenBitboard & Attack.Rook(kingSquare, OccupiedBitboard); // Determine pinning pieces by removing the first line of defence around the // the king, then seeing which pieces are able to attack. UInt64 defenceRemovedBitboard = OccupiedBitboard; UInt64 defenceBitboard = Bit.RayN[kingSquare] & selfBitboard; if (defenceBitboard != 0) { defenceRemovedBitboard ^= Bit.IsolateReverse(defenceBitboard); } defenceBitboard = Bit.RayE[kingSquare] & selfBitboard; if (defenceBitboard != 0) { defenceRemovedBitboard ^= Bit.Isolate(defenceBitboard); } defenceBitboard = Bit.RayS[kingSquare] & selfBitboard; if (defenceBitboard != 0) { defenceRemovedBitboard ^= Bit.Isolate(defenceBitboard); } defenceBitboard = Bit.RayW[kingSquare] & selfBitboard; if (defenceBitboard != 0) { defenceRemovedBitboard ^= Bit.IsolateReverse(defenceBitboard); } if (defenceRemovedBitboard != OccupiedBitboard) { pinningBitboard |= enemyRookQueenBitboard & Attack.Rook(kingSquare, defenceRemovedBitboard); } } // Consider castling. This is always fully tested for legality. if (checkingBitboard == 0) { Int32 rank = -56 * SideToMove + 56; if (CastleQueenside[SideToMove] > 0 && (Square[1 + rank] | Square[2 + rank] | Square[3 + rank]) == Piece.Empty) { if (!IsAttacked(SideToMove, 3 + rank) && !IsAttacked(SideToMove, 2 + rank)) { moves[index++] = Move.Create(this, kingSquare, 2 + rank, SideToMove | Piece.King); } } if (CastleKingside[SideToMove] > 0 && (Square[5 + rank] | Square[6 + rank]) == Piece.Empty) { if (!IsAttacked(SideToMove, 5 + rank) && !IsAttacked(SideToMove, 6 + rank)) { moves[index++] = Move.Create(this, kingSquare, 6 + rank, SideToMove | Piece.King); } } } // Consider en passant. This is always fully tested for legality. if (EnPassantSquare != InvalidSquare) { UInt64 enPassantPawnBitboard = Bitboard[SideToMove | Piece.Pawn] & Attack.Pawn(EnPassantSquare, enemy); UInt64 enPassantVictimBitboard = Move.Pawn(EnPassantSquare, enemy); while (enPassantPawnBitboard != 0) { // Perform minimal state changes to mimick en passant and check for // legality. Int32 from = Bit.Pop(ref enPassantPawnBitboard); Bitboard[enemy | Piece.Pawn] ^= enPassantVictimBitboard; OccupiedBitboard ^= enPassantVictimBitboard; OccupiedBitboard ^= (1UL << from) | (1UL << EnPassantSquare); // Check for legality and add move. if (!IsAttacked(SideToMove, kingSquare)) { moves[index++] = Move.Create(this, from, EnPassantSquare, enemy | Piece.Pawn); } // Revert state changes. Bitboard[enemy | Piece.Pawn] ^= enPassantVictimBitboard; OccupiedBitboard ^= enPassantVictimBitboard; OccupiedBitboard ^= (1UL << from) | (1UL << EnPassantSquare); } } // Consider king moves. This is always fully tested for legality. { Int32 from = kingSquare; UInt64 moveBitboard = targetBitboard & Attack.King(from); while (moveBitboard != 0) { // Perform minimal state changes to mimick real move and check for legality. Int32 to = Bit.Pop(ref moveBitboard); UInt64 occupiedBitboardCopy = OccupiedBitboard; Int32 capture = Square[to]; Bitboard[capture] ^= 1UL << to; OccupiedBitboard ^= 1UL << from; OccupiedBitboard |= 1UL << to; // Check for legality and add move. if (!IsAttacked(SideToMove, to)) { moves[index++] = Move.Create(this, from, to); } // Revert state changes. Bitboard[capture] ^= 1UL << to; OccupiedBitboard = occupiedBitboardCopy; } } // Case 1. If we are not in check and there are no pinned pieces, we don't // need to test normal moves for legality. if (checkingBitboard == 0 & pinningBitboard == 0) { // Consider normal pawn moves. UInt64 pieceBitboard = Bitboard[SideToMove | Piece.Pawn]; while (pieceBitboard != 0) { // Consider single square advance. Int32 from = Bit.Pop(ref pieceBitboard); Int32 to = from + 16 * SideToMove - 8; UInt64 moveBitboard = ~OccupiedBitboard & (1UL << to); // Consider two square advance. if (moveBitboard != 0 && (from - 16) * (from - 47) > 0 && (to - 8) * (to - 55) < 0) { moveBitboard |= ~OccupiedBitboard & (1UL << (from + 32 * SideToMove - 16)); } // Consider captures. UInt64 attackBitboard = Attack.Pawn(from, SideToMove); moveBitboard |= enemyBitboard & attackBitboard; // Populate pawn moves. while (moveBitboard != 0) { to = Bit.Pop(ref moveBitboard); if ((to - 8) * (to - 55) > 0) { moves[index++] = Move.Create(this, from, to, SideToMove | Piece.Queen); moves[index++] = Move.Create(this, from, to, SideToMove | Piece.Knight); moves[index++] = Move.Create(this, from, to, SideToMove | Piece.Rook); moves[index++] = Move.Create(this, from, to, SideToMove | Piece.Bishop); } else { moves[index++] = Move.Create(this, from, to); } } } // Consider knight moves. pieceBitboard = Bitboard[SideToMove | Piece.Knight]; while (pieceBitboard != 0) { Int32 from = Bit.Pop(ref pieceBitboard); UInt64 moveBitboard = targetBitboard & Attack.Knight(from); while (moveBitboard != 0) { Int32 to = Bit.Pop(ref moveBitboard); moves[index++] = Move.Create(this, from, to); } } // Consider bishop moves. pieceBitboard = Bitboard[SideToMove | Piece.Bishop]; while (pieceBitboard != 0) { Int32 from = Bit.Pop(ref pieceBitboard); UInt64 moveBitboard = targetBitboard & Attack.Bishop(from, OccupiedBitboard); while (moveBitboard != 0) { Int32 to = Bit.Pop(ref moveBitboard); moves[index++] = Move.Create(this, from, to); } } // Consider queen moves. pieceBitboard = Bitboard[SideToMove | Piece.Queen]; while (pieceBitboard != 0) { Int32 from = Bit.Pop(ref pieceBitboard); UInt64 moveBitboard = targetBitboard & Attack.Queen(from, OccupiedBitboard); while (moveBitboard != 0) { Int32 to = Bit.Pop(ref moveBitboard); moves[index++] = Move.Create(this, from, to); } } // Consider rook moves. pieceBitboard = Bitboard[SideToMove | Piece.Rook]; while (pieceBitboard != 0) { Int32 from = Bit.Pop(ref pieceBitboard); UInt64 moveBitboard = targetBitboard & Attack.Rook(from, OccupiedBitboard); while (moveBitboard != 0) { Int32 to = Bit.Pop(ref moveBitboard); moves[index++] = Move.Create(this, from, to); } } } // Case 2. There are pinned pieces or a single check. We can still move but // all moves are tested for legality. else if ((checkingBitboard & (checkingBitboard - 1)) == 0) { // Consider pawn moves. UInt64 pieceBitboard = Bitboard[SideToMove | Piece.Pawn]; while (pieceBitboard != 0) { // Consider single square advance. Int32 from = Bit.Pop(ref pieceBitboard); Int32 to = from + 16 * SideToMove - 8; UInt64 moveBitboard = ~OccupiedBitboard & (1UL << to); // Consider two square advance. if (moveBitboard != 0 && (from - 16) * (from - 47) > 0 && (to - 8) * (to - 55) < 0) { moveBitboard |= ~OccupiedBitboard & (1UL << (from + 32 * SideToMove - 16)); } // Consider captures. UInt64 attackBitboard = Attack.Pawn(from, SideToMove); moveBitboard |= enemyBitboard & attackBitboard; // Populate pawn moves. while (moveBitboard != 0) { // Perform minimal state changes to mimick real move and check for legality. to = Bit.Pop(ref moveBitboard); UInt64 occupiedBitboardCopy = OccupiedBitboard; Int32 capture = Square[to]; Bitboard[capture] ^= 1UL << to; OccupiedBitboard ^= 1UL << from; OccupiedBitboard |= 1UL << to; // Check for legality and add moves. if (!IsAttacked(SideToMove, kingSquare)) { if ((to - 8) * (to - 55) > 0) { moves[index++] = Move.Create(this, from, to, SideToMove | Piece.Queen); moves[index++] = Move.Create(this, from, to, SideToMove | Piece.Knight); moves[index++] = Move.Create(this, from, to, SideToMove | Piece.Rook); moves[index++] = Move.Create(this, from, to, SideToMove | Piece.Bishop); } else { moves[index++] = Move.Create(this, from, to); } } // Revert state changes. Bitboard[capture] ^= 1UL << to; OccupiedBitboard = occupiedBitboardCopy; } } // Consider knight moves. pieceBitboard = Bitboard[SideToMove | Piece.Knight]; while (pieceBitboard != 0) { Int32 from = Bit.Pop(ref pieceBitboard); UInt64 moveBitboard = targetBitboard & Attack.Knight(from); while (moveBitboard != 0) { // Perform minimal state changes to mimick real move and check for legality. Int32 to = Bit.Pop(ref moveBitboard); UInt64 occupiedBitboardCopy = OccupiedBitboard; Int32 capture = Square[to]; Bitboard[capture] ^= 1UL << to; OccupiedBitboard ^= 1UL << from; OccupiedBitboard |= 1UL << to; // Check for legality and add move. if (!IsAttacked(SideToMove, kingSquare)) { moves[index++] = Move.Create(this, from, to); } // Revert state changes. Bitboard[capture] ^= 1UL << to; OccupiedBitboard = occupiedBitboardCopy; } } // Consider bishop moves. pieceBitboard = Bitboard[SideToMove | Piece.Bishop]; while (pieceBitboard != 0) { Int32 from = Bit.Pop(ref pieceBitboard); UInt64 moveBitboard = targetBitboard & Attack.Bishop(from, OccupiedBitboard); while (moveBitboard != 0) { // Perform minimal state changes to mimick real move and check for legality. Int32 to = Bit.Pop(ref moveBitboard); UInt64 occupiedBitboardCopy = OccupiedBitboard; Int32 capture = Square[to]; Bitboard[capture] ^= 1UL << to; OccupiedBitboard ^= 1UL << from; OccupiedBitboard |= 1UL << to; // Check for legality and add move. if (!IsAttacked(SideToMove, kingSquare)) { moves[index++] = Move.Create(this, from, to); } // Revert state changes. Bitboard[capture] ^= 1UL << to; OccupiedBitboard = occupiedBitboardCopy; } } // Consider queen moves. pieceBitboard = Bitboard[SideToMove | Piece.Queen]; while (pieceBitboard != 0) { Int32 from = Bit.Pop(ref pieceBitboard); UInt64 moveBitboard = targetBitboard & Attack.Queen(from, OccupiedBitboard); while (moveBitboard != 0) { // Perform minimal state changes to mimick real move and check for legality. Int32 to = Bit.Pop(ref moveBitboard); UInt64 occupiedBitboardCopy = OccupiedBitboard; Int32 capture = Square[to]; Bitboard[capture] ^= 1UL << to; OccupiedBitboard ^= 1UL << from; OccupiedBitboard |= 1UL << to; // Check for legality and add move. if (!IsAttacked(SideToMove, kingSquare)) { moves[index++] = Move.Create(this, from, to); } // Revert state changes. Bitboard[capture] ^= 1UL << to; OccupiedBitboard = occupiedBitboardCopy; } } // Consider rook moves. pieceBitboard = Bitboard[SideToMove | Piece.Rook]; while (pieceBitboard != 0) { Int32 from = Bit.Pop(ref pieceBitboard); UInt64 moveBitboard = targetBitboard & Attack.Rook(from, OccupiedBitboard); while (moveBitboard != 0) { // Perform minimal state changes to mimick real move and check for legality. Int32 to = Bit.Pop(ref moveBitboard); UInt64 occupiedBitboardCopy = OccupiedBitboard; Int32 capture = Square[to]; Bitboard[capture] ^= 1UL << to; OccupiedBitboard ^= 1UL << from; OccupiedBitboard |= 1UL << to; // Check for legality and add move. if (!IsAttacked(SideToMove, kingSquare)) { moves[index++] = Move.Create(this, from, to); } // Revert state changes. Bitboard[capture] ^= 1UL << to; OccupiedBitboard = occupiedBitboardCopy; } } } return(index); }
/// <summary> /// Populates the given array with the pseudo-legal capturing and queen /// promotion moves for the position and returns the number of moves. /// </summary> /// <param name="moves">The array to populate with the pseudo-legal moves.</param> /// <returns>The number of moves generated for the position.</returns> public Int32 PseudoQuiescenceMoves(Int32[] moves) { UInt64 targetBitboard = Bitboard[(1 - SideToMove)]; Int32 index = 0; // Consider king moves. UInt64 pieceBitboard = Bitboard[SideToMove | Piece.King]; Int32 from = Bit.Read(pieceBitboard); UInt64 moveBitboard = targetBitboard & Attack.King(from); while (moveBitboard != 0) { Int32 to = Bit.Pop(ref moveBitboard); moves[index++] = Move.Create(this, from, to); } // Consider queen moves. pieceBitboard = Bitboard[SideToMove | Piece.Queen]; while (pieceBitboard != 0) { from = Bit.Pop(ref pieceBitboard); moveBitboard = targetBitboard & Attack.Queen(from, OccupiedBitboard); while (moveBitboard != 0) { Int32 to = Bit.Pop(ref moveBitboard); moves[index++] = Move.Create(this, from, to); } } // Consider rook moves. pieceBitboard = Bitboard[SideToMove | Piece.Rook]; while (pieceBitboard != 0) { from = Bit.Pop(ref pieceBitboard); moveBitboard = targetBitboard & Attack.Rook(from, OccupiedBitboard); while (moveBitboard != 0) { Int32 to = Bit.Pop(ref moveBitboard); moves[index++] = Move.Create(this, from, to); } } // Consider knight moves. pieceBitboard = Bitboard[SideToMove | Piece.Knight]; while (pieceBitboard != 0) { from = Bit.Pop(ref pieceBitboard); moveBitboard = targetBitboard & Attack.Knight(from); while (moveBitboard != 0) { Int32 to = Bit.Pop(ref moveBitboard); moves[index++] = Move.Create(this, from, to); } } // Consider bishop moves. pieceBitboard = Bitboard[SideToMove | Piece.Bishop]; while (pieceBitboard != 0) { from = Bit.Pop(ref pieceBitboard); moveBitboard = targetBitboard & Attack.Bishop(from, OccupiedBitboard); while (moveBitboard != 0) { Int32 to = Bit.Pop(ref moveBitboard); moves[index++] = Move.Create(this, from, to); } } // Consider pawn moves. pieceBitboard = Bitboard[SideToMove | Piece.Pawn]; while (pieceBitboard != 0) { from = Bit.Pop(ref pieceBitboard); moveBitboard = targetBitboard & Attack.Pawn(from, SideToMove); Int32 to = from + 16 * SideToMove - 8; Boolean promotion = (to - 8) * (to - 55) > 0; if (promotion) { moveBitboard |= ~OccupiedBitboard & (1UL << to); } while (moveBitboard != 0) { to = Bit.Pop(ref moveBitboard); if (promotion) { moves[index++] = Move.Create(this, from, to, SideToMove | Piece.Queen); } else { moves[index++] = Move.Create(this, from, to); } } } return(index); }
/// <summary> /// Executes the parsing. /// </summary> public static void Run() { Restrictions.Output = OutputType.Universal; IEngine engine = new Zero(); Position position = new Position(Position.StartingFEN); String command; while ((command = Console.ReadLine()) != null) { List <String> terms = new List <String>(command.Split(' ')); switch (terms[0]) { default: Terminal.WriteLine("Unknown command: {0}", terms[0]); Terminal.WriteLine("Enter \"help\" for assistance."); break; case "uci": Terminal.WriteLine("id name " + engine.Name); Terminal.WriteLine("id author Zong Zheng Li"); Terminal.WriteLine("option name Hash type spin default " + Zero.DefaultHashAllocation + " min 1 max 2047"); Terminal.WriteLine("uciok"); break; case "ucinewgame": engine.Reset(); break; case "setoption": if (terms.Contains("Hash")) { engine.HashAllocation = Int32.Parse(terms[terms.IndexOf("value") + 1]); } break; case "position": String fen = Position.StartingFEN; if (terms[1] != "startpos") { fen = command.Substring(command.IndexOf("fen") + 4); } position = new Position(fen); Int32 movesIndex = terms.IndexOf("moves"); if (movesIndex >= 0) { for (Int32 i = movesIndex + 1; i < terms.Count; i++) { position.Make(Move.Create(position, terms[i])); } } break; case "go": Restrictions.Reset(); for (Int32 i = 1; i < terms.Count; i++) { switch (terms[i]) { default: case "infinite": break; case "depth": Restrictions.Depth = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = false; break; case "movetime": Restrictions.MoveTime = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = false; break; case "wtime": Restrictions.TimeControl[Colour.White] = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = true; break; case "btime": Restrictions.TimeControl[Colour.Black] = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = true; break; case "winc": Restrictions.TimeIncrement[Colour.White] = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = true; break; case "binc": Restrictions.TimeIncrement[Colour.Black] = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = true; break; case "nodes": Restrictions.Nodes = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = false; break; case "ponder": // TODO: implement command. break; case "mate": // TODO: implement command. break; case "movestogo": // TODO: implement command. break; } } new Thread(new ThreadStart(() => { Int32 bestMove = engine.GetMove(position); Terminal.WriteLine("bestmove " + Stringify.Move(bestMove)); })) { IsBackground = true }.Start(); break; case "stop": engine.Stop(); break; case "isready": Terminal.WriteLine("readyok"); break; case "quit": return; case "perft": Perft.Iterate(position, Int32.Parse(terms[1])); break; case "divide": Perft.Divide(position, Int32.Parse(terms[1])); break; case "draw": Terminal.WriteLine(position); break; case "fen": Terminal.WriteLine(position.GetFEN()); break; case "ponderhit": // TODO: implement command. break; case "register": // TODO: implement command. break; case "help": Terminal.WriteLine("Command Function"); Terminal.WriteLine("-----------------------------------------------------------------------"); Terminal.WriteLine("position [fen] Sets the current position to the position denoted"); Terminal.WriteLine(" by the given FEN. \"startpos\" is accepted for the"); Terminal.WriteLine(" starting position"); Terminal.WriteLine("go [type] [number] Searches the current position. Search types include"); Terminal.WriteLine(" \"movetime\", \"depth\", \"nodes\", \"wtime\", \"btime\","); Terminal.WriteLine(" \"winc\", and \"binc\""); Terminal.WriteLine("perft [number] Runs perft() on the current position to the given"); Terminal.WriteLine(" depth"); Terminal.WriteLine("divide [number] Runs divide() on the current position for the given"); Terminal.WriteLine(" depth"); Terminal.WriteLine("fen Prints the FEN of the current position."); Terminal.WriteLine("draw Draws the current position"); Terminal.WriteLine("stop Stops an ongoing search"); Terminal.WriteLine("quit Exits the application"); Terminal.WriteLine("-----------------------------------------------------------------------"); break; } } }