internal void copy(Position pos) { // MEMCPY Array.Copy(pos.board, this.board, 64); Array.Copy(pos.byTypeBB, this.byTypeBB, 8); Array.Copy(pos.byColorBB, this.byColorBB, 2); this.occupied_squares = pos.occupied_squares; for (int i = 0; i < 2; i++) { Array.Copy(pos.castleRookSquare[i], this.castleRookSquare[i], 2); Array.Copy(pos.castlePath[i], this.castlePath[i], 2); Array.Copy(pos.pieceCount[i], this.pieceCount[i], 8); for (int j = 0; j < 8; j++) { Array.Copy(pos.pieceList[i][j], this.pieceList[i][j], 16); } } Array.Copy(pos.index, this.index, 64); Array.Copy(pos.castleRightsMask, this.castleRightsMask, 64); this.startState = pos.startState; this.startPosPly = pos.startPosPly; this.sideToMove = pos.sideToMove; this.st = pos.st; this.chess960 = pos.chess960; this.thisThread = pos.thisThread; startState = st; st = startState; nodes = 0; Debug.Assert(pos_is_ok()); }
internal void Clear() { this.pawnKey = 0; this.materialKey = 0; this.npMaterialWHITE = 0; this.npMaterialBLACK = 0; this.castleRights = 0; this.rule50 = 0; this.pliesFromNull = 0; this.psqScore = 0; this.epSquare = 0; this.key = 0; this.checkersBB = 0; this.capturedType = 0; this.previous = null; }
internal void do_move(int m, StateInfo newSt) { var ci = CheckInfoBroker.GetObject(); ci.CreateCheckInfo(this); this.do_move(m, newSt, ci, this.move_gives_check(m, ci)); CheckInfoBroker.Free(); }
/// clear() erases the position object to a pristine state, with an /// empty board, white to move, and no castling rights. internal void clear() { Array.Clear(byColorBB, 0, 2); Array.Clear(byTypeBB, 0, 8); Array.Clear(pieceCount[0], 0, 8); Array.Clear(pieceCount[1], 0, 8); Array.Clear(index, 0, 64); Array.Clear(castleRightsMask, 0, 64); Array.Clear(castleRookSquare[0], 0, 2); Array.Clear(castleRookSquare[1], 0, 2); Array.Clear(castlePath[0], 0, 2); Array.Clear(castlePath[1], 0, 2); startState.Clear(); occupied_squares = 0; nodes = 0; startPosPly = 0; sideToMove = 0; thisThread = null; chess960 = false; startState.epSquare = SquareC.SQ_NONE; st = startState; for (int i = 0; i < 8; i++) for (int j = 0; j < 16; j++) pieceList[0][i][j] = pieceList[1][i][j] = SquareC.SQ_NONE; for (Square sq = SquareC.SQ_A1; sq <= SquareC.SQ_H8; sq++) board[sq] = PieceC.NO_PIECE; }
/// Wait for a command from the user, parse this text string as an UCI command, /// and call the appropriate functions. Also intercepts EOF from stdin to ensure /// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI /// commands, the function also supports a few debug commands. internal static void uci_loop(string args) { for (int i = 0; i < 102; i++) { StateRingBuf[i] = new StateInfo(); } Position pos = new Position(StartFEN, false, Threads.main_thread()); // The root position string cmd, token = string.Empty; while (token != "quit") { if (args.Length > 0) { cmd = args; } else if (String.IsNullOrEmpty(cmd = Plug.ReadLine())) // Block here waiting for input { cmd = "quit"; } Stack<string> stack = Utils.CreateStack(cmd); token = stack.Pop(); if (token == "quit" || token == "stop") { Search.SignalsStop = true; Threads.wait_for_search_finished(); // Cannot quit while threads are running } else if (token == "ponderhit") { // The opponent has played the expected move. GUI sends "ponderhit" if // we were told to ponder on the same move the opponent has played. We // should continue searching but switching from pondering to normal search. Search.Limits.ponder = false; if (Search.SignalsStopOnPonderhit) { Search.SignalsStop = true; Threads.main_thread().wake_up(); // Could be sleeping } } else if (token == "go") { go(pos, stack); } else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ } else if (token == "isready") { Plug.Write("readyok"); Plug.Write(Constants.endl); } else if (token == "position") { set_position(pos, stack); } else if (token == "setoption") { set_option(stack); } else if (token == "validmoves") { Search.validmoves(pos, stack); } else if (token == "d") { pos.print(0); } else if (token == "flip") { pos.flip(); } else if (token == "eval") { Plug.Write(Evaluate.trace(pos)); Plug.Write(Constants.endl); } else if (token == "bench") { Benchmark.benchmark(pos, stack); } else if (token == "key") { Plug.Write("key: "); Plug.Write(String.Format("{0:X}", pos.key())); Plug.Write("\nmaterial key: "); Plug.Write(pos.material_key().ToString()); Plug.Write("\npawn key: "); Plug.Write(pos.pawn_key().ToString()); Plug.Write(Constants.endl); } else if (token == "uci") { Plug.Write("id name "); Plug.Write(Utils.engine_info(true)); Plug.Write("\n"); Plug.Write(OptionMap.Instance.ToString()); Plug.Write("\nuciok"); Plug.Write(Constants.endl); } else if (token == "perft") { token = stack.Pop(); // Read depth Stack<string> ss = Utils.CreateStack( string.Format("{0} {1} {2} current perft", OptionMap.Instance["Hash"].v, OptionMap.Instance["Threads"].v, token) ); Benchmark.benchmark(pos, ss); } else { Plug.Write("Unknown command: "); Plug.Write(cmd); Plug.Write(Constants.endl); } if (args.Length > 0) // Command line arguments have one-shot behaviour { Threads.wait_for_search_finished(); break; } } }
internal static StateInfo GetObject() { int slotID = System.Threading.Thread.CurrentThread.ManagedThreadId & Constants.BROKER_SLOT_MASK; if (_cnt[slotID] == _pool[slotID].Length) { int poolLength = _pool[slotID].Length; StateInfo[] temp = new StateInfo[poolLength + Constants.BrokerCapacity]; Array.Copy(_pool[slotID], temp, poolLength); for (int i = 0; i < Constants.BrokerCapacity; i++) { temp[poolLength + i] = new StateInfo(); } _pool[slotID] = temp; } return _pool[slotID][_cnt[slotID]++]; }
internal void do_move(Move m, StateInfo newSt) { CheckInfo ci = CheckInfoBroker.GetObject(); ci.CreateCheckInfo(this); do_move(m, newSt, ci, move_gives_check(m, ci)); CheckInfoBroker.Free(); }
/// Search::perft() is our utility to verify move generation. All the leaf nodes /// up to the given depth are generated and counted and the sum returned. internal static Int64 perft(Position pos, Depth depth) { StateInfo st = new StateInfo(); Int64 cnt = 0; MList mlist = MListBroker.GetObject(); mlist.pos = 0; Movegen.generate_legal(pos, mlist.moves, ref mlist.pos); // At the last ply just return the number of moves (leaf nodes) if (depth == DepthC.ONE_PLY) { int retval = mlist.pos; MListBroker.Free(); return retval; } CheckInfo ci = CheckInfoBroker.GetObject(); ci.CreateCheckInfo(pos); for (int i = 0; i < mlist.pos; ++i) { MoveStack ms = mlist.moves[i]; pos.do_move(ms.move, st, ci, pos.move_gives_check(ms.move, ci)); cnt += perft(pos, depth - DepthC.ONE_PLY); pos.undo_move(ms.move); } CheckInfoBroker.Free(); MListBroker.Free(); return cnt; }
// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips // the side to move without executing any move on the board. internal void do_null_move(StateInfo newSt) { Debug.Assert(!this.in_check()); newSt.capturedType = st.capturedType; newSt.castleRights = st.castleRights; newSt.rule50 = st.rule50; newSt.pliesFromNull = st.pliesFromNull; newSt.checkersBB = st.checkersBB; newSt.epSquare = st.epSquare; newSt.key = st.key; newSt.npMaterialWHITE = st.npMaterialWHITE; newSt.npMaterialBLACK = st.npMaterialBLACK; newSt.pawnKey = st.pawnKey; newSt.materialKey = st.materialKey; newSt.psqScore = st.psqScore; newSt.previous = st; st = newSt; if (this.st.epSquare != SquareC.SQ_NONE) { this.st.key ^= Zobrist.enpassant[Utils.file_of(this.st.epSquare)]; this.st.epSquare = SquareC.SQ_NONE; } st.key ^= Zobrist.side; st.rule50++; st.pliesFromNull = 0; this.sideToMove = Utils.flip_C(this.sideToMove); Debug.Assert(this.pos_is_ok()); }
/// clear() erases the position object to a pristine state, with an /// empty board, white to move, and no castling rights. internal void clear() { Array.Clear(this.byColorBB, 0, 2); Array.Clear(this.byTypeBB, 0, 8); Array.Clear(this.pieceCount[0], 0, 8); Array.Clear(this.pieceCount[1], 0, 8); Array.Clear(this.index, 0, 64); Array.Clear(this.castleRightsMask, 0, 64); Array.Clear(this.castleRookSquare[0], 0, 2); Array.Clear(this.castleRookSquare[1], 0, 2); Array.Clear(this.castlePath[0], 0, 2); Array.Clear(this.castlePath[1], 0, 2); this.startState.Clear(); this.occupied_squares = 0; this.nodes = 0; this.startPosPly = 0; this.sideToMove = 0; this.thisThread = null; this.chess960 = false; this.startState.epSquare = SquareC.SQ_NONE; this.st = this.startState; for (var i = 0; i < 8; i++) { for (var j = 0; j < 16; j++) { this.pieceList[0][i][j] = this.pieceList[1][i][j] = SquareC.SQ_NONE; } } for (var sq = SquareC.SQ_A1; sq <= SquareC.SQ_H8; sq++) { this.board[sq] = PieceC.NO_PIECE; } }
internal void undo_null_move(StateInfo backupSt) { Debug.Assert(!this.in_check()); st = st.previous; this.sideToMove = Utils.flip_C(this.sideToMove); }
/// undo_move() unmakes a move. When it returns, the position should /// be restored to exactly the same state as before the move was made. internal void undo_move(int m) { Debug.Assert(Utils.is_ok_M(m)); this.sideToMove = this.sideToMove ^ 1; var us = this.sideToMove; var them = us ^ 1; var from = ((m >> 6) & 0x3F); var to = (m & 0x3F); var pt = Utils.type_of(piece_on(to)); var capture = this.st.capturedType; Debug.Assert(this.is_empty(from) || Utils.type_of_move(m) == MoveTypeC.CASTLING); Debug.Assert(capture != PieceTypeC.KING); if (Utils.type_of_move(m) == MoveTypeC.PROMOTION) { var promotion = (((m >> 12) & 3) + 2); Debug.Assert(promotion == pt); Debug.Assert(Utils.relative_rank_CS(us, to) == RankC.RANK_8); Debug.Assert(promotion >= PieceTypeC.KNIGHT && promotion <= PieceTypeC.QUEEN); // Replace the promoted piece with the pawn this.byTypeBB[promotion] ^= Utils.SquareBB[to]; this.byTypeBB[PieceTypeC.PAWN] |= Utils.SquareBB[to]; this.board[to] = ((us << 3) | PieceTypeC.PAWN); // Update piece lists, move the last promoted piece at index[to] position // and shrink the list. Add a new pawn to the list. var lastSquare = this.pieceList[us][promotion][--this.pieceCount[us][promotion]]; this.index[lastSquare] = this.index[to]; var plUsPromotion = this.pieceList[us][promotion]; plUsPromotion[this.index[lastSquare]] = lastSquare; plUsPromotion[this.pieceCount[us][promotion]] = SquareC.SQ_NONE; this.index[to] = this.pieceCount[us][PieceTypeC.PAWN]++; this.pieceList[us][PieceTypeC.PAWN][this.index[to]] = to; pt = PieceTypeC.PAWN; } if (Utils.type_of_move(m) == MoveTypeC.CASTLING) { bool kingSide = to > from; Square rfrom = to; // Castle is encoded as "king captures friendly rook" Square rto = Utils.relative_square(us, kingSide ? SquareC.SQ_F1 : SquareC.SQ_D1); to = Utils.relative_square(us, kingSide ? SquareC.SQ_G1 : SquareC.SQ_C1); capture = PieceTypeC.NO_PIECE_TYPE; pt = PieceTypeC.KING; do_castle(to, from, rto, rfrom); } else { // Put the piece back at the source square Bitboard from_to_bb = Utils.SquareBB[from] ^ Utils.SquareBB[to]; occupied_squares ^= from_to_bb; byTypeBB[pt] ^= from_to_bb; byColorBB[us] ^= from_to_bb; board[to] = PieceC.NO_PIECE; board[from] = Utils.make_piece(us, pt); // Update piece lists, index[to] is not updated and becomes stale. This // works as long as index[] is accessed just by known occupied squares. index[from] = index[to]; pieceList[us][pt][index[from]] = from; } if (capture != 0) { var capsq = to; if (Utils.type_of_move(m) == MoveTypeC.ENPASSANT) { capsq -= (us == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S); Debug.Assert(pt == PieceTypeC.PAWN); Debug.Assert(to == this.st.previous.epSquare); Debug.Assert(Utils.relative_rank_CS(us, to) == RankC.RANK_6); Debug.Assert(this.piece_on(capsq) == PieceC.NO_PIECE); } // Restore the captured piece var capSqMask = Utils.SquareBB[capsq]; this.occupied_squares |= capSqMask; this.byTypeBB[capture] |= capSqMask; this.byColorBB[them] |= capSqMask; this.board[capsq] = ((them << 3) | capture); // Update piece list, add a new captured piece in capsq square this.index[capsq] = this.pieceCount[them][capture]++; this.pieceList[them][capture][this.index[capsq]] = capsq; } // Finally point our state pointer back to the previous state this.st = this.st.previous; Debug.Assert(this.pos_is_ok()); }
internal void do_move(int m, StateInfo newSt, CheckInfo ci, bool moveIsCheck) { Debug.Assert(Utils.is_ok_M(m)); Debug.Assert(newSt != this.st); this.nodes++; var k = this.st.key; newSt.pawnKey = this.st.pawnKey; newSt.materialKey = this.st.materialKey; newSt.npMaterialWHITE = this.st.npMaterialWHITE; newSt.npMaterialBLACK = this.st.npMaterialBLACK; newSt.castleRights = this.st.castleRights; newSt.rule50 = this.st.rule50; newSt.pliesFromNull = this.st.pliesFromNull; newSt.psqScore = this.st.psqScore; newSt.epSquare = this.st.epSquare; newSt.previous = this.st; this.st = newSt; // Update side to move k ^= Zobrist.side; // Increment the 50 moves rule draw counter. Resetting it to zero in the // case of a capture or a pawn move is taken care of later. this.st.rule50++; this.st.pliesFromNull++; var us = this.sideToMove; var them = us ^ 1; Square from = Utils.from_sq(m); Square to = Utils.to_sq(m); Piece piece = piece_on(from); PieceType pt = Utils.type_of(piece); PieceType capture = Utils.type_of_move(m) == MoveTypeC.ENPASSANT ? PieceTypeC.PAWN : Utils.type_of(piece_on(to)); Debug.Assert(Utils.color_of(piece) == us); Debug.Assert(piece_on(to) == PieceC.NO_PIECE || Utils.color_of(piece_on(to)) == them || Utils.type_of_move(m) == MoveTypeC.CASTLING); Debug.Assert(capture != PieceTypeC.KING); if (Utils.type_of_move(m) == MoveTypeC.CASTLING) { Debug.Assert(piece == Utils.make_piece(us, PieceTypeC.KING)); bool kingSide = to > from; Square rfrom = to; // Castle is encoded as "king captures friendly rook" Square rto = Utils.relative_square(us, kingSide ? SquareC.SQ_F1 : SquareC.SQ_D1); to = Utils.relative_square(us, kingSide ? SquareC.SQ_G1 : SquareC.SQ_C1); capture = PieceTypeC.NO_PIECE_TYPE; do_castle(from, to, rfrom, rto); st.psqScore += psq_delta(Utils.make_piece(us, PieceTypeC.ROOK), rfrom, rto); k ^= Zobrist.psq[us][PieceTypeC.ROOK][rfrom] ^ Zobrist.psq[us][PieceTypeC.ROOK][rto]; } if (capture != 0) { var capsq = to; // If the captured piece is a pawn, update pawn hash key, otherwise // update non-pawn material. if (capture == PieceTypeC.PAWN) { if (Utils.type_of_move(m) == MoveTypeC.ENPASSANT) { capsq += (them == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S); Debug.Assert(pt == PieceTypeC.PAWN); Debug.Assert(to == this.st.epSquare); Debug.Assert(Utils.relative_rank_CS(us, to) == RankC.RANK_6); Debug.Assert(this.piece_on(to) == PieceC.NO_PIECE); Debug.Assert(this.piece_on(capsq) == Utils.make_piece(them, PieceTypeC.PAWN)); this.board[capsq] = PieceC.NO_PIECE; } this.st.pawnKey ^= Zobrist.psq[them][PieceTypeC.PAWN][capsq]; } else { if (them == 0) { this.st.npMaterialWHITE -= PieceValue[PhaseC.MG][capture]; } else { this.st.npMaterialBLACK -= PieceValue[PhaseC.MG][capture]; } } // Remove the captured piece var capPieceMask = Utils.SquareBB[capsq]; this.occupied_squares ^= capPieceMask; this.byTypeBB[capture] ^= capPieceMask; this.byColorBB[them] ^= capPieceMask; // Update piece list, move the last piece at index[capsq] position and // shrink the list. // // WARNING: This is a not revresible operation. When we will reinsert the // captured piece in undo_move() we will put it at the end of the list and // not in its original place, it means index[] and pieceList[] are not // guaranteed to be invariant to a do_move() + undo_move() sequence. var plThemCapture = this.pieceList[them][capture]; var pcThemCapture = --this.pieceCount[them][capture]; var lastSquare = plThemCapture[pcThemCapture]; this.index[lastSquare] = this.index[capsq]; plThemCapture[this.index[lastSquare]] = lastSquare; plThemCapture[pcThemCapture] = SquareC.SQ_NONE; // Update hash keys k ^= Zobrist.psq[them][capture][capsq]; this.st.materialKey ^= Zobrist.psq[them][capture][pcThemCapture]; // Update incremental scores this.st.psqScore -= Zobrist.PieceSquareTable[((them << 3) | capture)][capsq]; // Reset rule 50 counter this.st.rule50 = 0; } // Update hash key k ^= Zobrist.psq[us][pt][from] ^ Zobrist.psq[us][pt][to]; // Reset en passant square if (this.st.epSquare != SquareC.SQ_NONE) { k ^= Zobrist.enpassant[this.st.epSquare & 7]; this.st.epSquare = SquareC.SQ_NONE; } // Update castle rights if needed if ((this.st.castleRights != 0) && ((this.castleRightsMask[from] | this.castleRightsMask[to]) != 0)) { var cr = this.castleRightsMask[from] | this.castleRightsMask[to]; k ^= Zobrist.castle[this.st.castleRights & cr]; this.st.castleRights &= ~cr; } // Move the piece. The tricky Chess960 castle is handled earlier if (Utils.type_of_move(m) != MoveTypeC.CASTLING) { Bitboard from_to_bb = Utils.SquareBB[from] ^ Utils.SquareBB[to]; occupied_squares ^= from_to_bb; byTypeBB[pt] ^= from_to_bb; byColorBB[us] ^= from_to_bb; board[from] = PieceC.NO_PIECE; board[to] = piece; // Update piece lists, index[from] is not updated and becomes stale. This // works as long as index[] is accessed just by known occupied squares. index[to] = index[from]; pieceList[us][pt][index[to]] = to; } // If the moving piece is a pawn do some special extra work if (pt == PieceTypeC.PAWN) { // Set en-passant square, only if moved pawn can be captured if ((to ^ from) == 16 && ((((Utils.StepAttacksBB[((us << 3) | PieceTypeC.PAWN)][ from + (us == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S)]) & (this.byTypeBB[PieceTypeC.PAWN] & this.byColorBB[them]))) != 0)) { this.st.epSquare = ((from + to) / 2); k ^= Zobrist.enpassant[this.st.epSquare & 7]; } if (Utils.type_of_move(m) == MoveTypeC.PROMOTION) { var promotion = (((m >> 12) & 3) + 2); Debug.Assert(Utils.relative_rank_CS(us, to) == RankC.RANK_8); Debug.Assert(promotion >= PieceTypeC.KNIGHT && promotion <= PieceTypeC.QUEEN); // Replace the pawn with the promoted piece this.byTypeBB[PieceTypeC.PAWN] ^= Utils.SquareBB[to]; this.byTypeBB[promotion] |= Utils.SquareBB[to]; this.board[to] = ((us << 3) | promotion); // Update piece lists, move the last pawn at index[to] position // and shrink the list. Add a new promotion piece to the list. var plUsPawn = this.pieceList[us][PieceTypeC.PAWN]; var lastSquare = plUsPawn[--this.pieceCount[us][PieceTypeC.PAWN]]; this.index[lastSquare] = this.index[to]; plUsPawn[this.index[lastSquare]] = lastSquare; plUsPawn[this.pieceCount[us][PieceTypeC.PAWN]] = SquareC.SQ_NONE; this.index[to] = this.pieceCount[us][promotion]; this.pieceList[us][promotion][this.index[to]] = to; // Update hash keys k ^= Zobrist.psq[us][PieceTypeC.PAWN][to] ^ Zobrist.psq[us][promotion][to]; this.st.pawnKey ^= Zobrist.psq[us][PieceTypeC.PAWN][to]; this.st.materialKey ^= Zobrist.psq[us][promotion][this.pieceCount[us][promotion]++] ^ Zobrist.psq[us][PieceTypeC.PAWN][this.pieceCount[us][PieceTypeC.PAWN]]; // Update incremental score this.st.psqScore += Zobrist.PieceSquareTable[((us << 3) | promotion)][to] - Zobrist.PieceSquareTable[((us << 3) | PieceTypeC.PAWN)][to]; // Update material if (us == 0) { this.st.npMaterialWHITE += PieceValue[PhaseC.MG][promotion]; } else { this.st.npMaterialBLACK += PieceValue[PhaseC.MG][promotion]; } } // Update pawn hash key this.st.pawnKey ^= Zobrist.psq[us][PieceTypeC.PAWN][from] ^ Zobrist.psq[us][PieceTypeC.PAWN][to]; // Reset rule 50 draw counter this.st.rule50 = 0; } // Update incremental scores this.st.psqScore += (Zobrist.PieceSquareTable[piece][to] - Zobrist.PieceSquareTable[piece][from]); // Set capture piece this.st.capturedType = capture; // Update the key with the final value this.st.key = k; // Update checkers bitboard, piece must be already moved this.st.checkersBB = 0; if (moveIsCheck) { if (Utils.type_of_move(m) != MoveTypeC.NORMAL) { this.st.checkersBB = this.attackers_to(this.pieceList[them][PieceTypeC.KING][0]) & this.byColorBB[us]; } else { // Direct checks if ((ci.checkSq[pt] & Utils.SquareBB[to]) != 0) { this.st.checkersBB |= Utils.SquareBB[to]; } // Discovery checks if ((ci.dcCandidates != 0) && ((ci.dcCandidates & Utils.SquareBB[from]) != 0)) { if (pt != PieceTypeC.ROOK) { this.st.checkersBB |= this.attacks_from_ROOK(this.pieceList[them][PieceTypeC.KING][0]) & ((this.byTypeBB[PieceTypeC.ROOK] | this.byTypeBB[PieceTypeC.QUEEN]) & this.byColorBB[us]); } if (pt != PieceTypeC.BISHOP) { this.st.checkersBB |= this.attacks_from_BISHOP(this.pieceList[them][PieceTypeC.KING][0]) & ((this.byTypeBB[PieceTypeC.BISHOP] | this.byTypeBB[PieceTypeC.QUEEN]) & this.byColorBB[us]); } } } } // Finish this.sideToMove = this.sideToMove ^ 1; Debug.Assert(this.pos_is_ok()); }
/// Wait for a command from the user, parse this text string as an UCI command, /// and call the appropriate functions. Also intercepts EOF from stdin to ensure /// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI /// commands, the function also supports a few debug commands. internal static void loop(string args) { for (var i = 0; i < 102; i++) { StateRingBuf[i] = new StateInfo(); } var pos = new Position(StartFEN, false, Threads.main_thread()); // The root position string cmd, token = string.Empty; while (token != "quit") { if (args.Length > 0) { cmd = args; } else if (string.IsNullOrEmpty(cmd = Plug.ReadLine())) // Block here waiting for input { cmd = "quit"; } var stack = Utils.CreateStack(cmd); token = stack.Pop(); if (token == "quit" || token == "stop" || token == "ponderhit") { // GUI sends 'ponderhit' to tell us to ponder on the same move the // opponent has played. In case Signals.stopOnPonderhit is set we are // waiting for 'ponderhit' to stop the search (for instance because we // already ran out of time), otherwise we should continue searching but // switching from pondering to normal search. if (token != "ponderhit" || Search.SignalsStopOnPonderhit) { Search.SignalsStop = true; Threads.main_thread().notify_one(); // Could be sleeping } else { Search.Limits.ponder = false; } } else if (token == "go") { go(pos, stack); } else if (token == "ucinewgame") { TT.clear(); } else if (token == "isready") { Plug.Write("readyok"); Plug.Write(Constants.endl); } else if (token == "position") { set_position(pos, stack); } else if (token == "setoption") { set_option(stack); } else if (token == "validmoves") { Search.validmoves(pos, stack); } else if (token == "d") { pos.print(0); } else if (token == "flip") { pos.flip(); } else if (token == "eval") { Plug.Write(Evaluate.trace(pos)); Plug.Write(Constants.endl); } else if (token == "bench") { Benchmark.benchmark(pos, stack); } else if (token == "key") { Plug.Write("key: "); Plug.Write(string.Format("{0:X}", pos.key())); Plug.Write("\nmaterial key: "); Plug.Write(pos.material_key().ToString()); Plug.Write("\npawn key: "); Plug.Write(pos.pawn_key().ToString()); Plug.Write(Constants.endl); } else if (token == "uci") { Plug.Write("id name "); Plug.Write(Utils.engine_info(true)); Plug.Write("\n"); Plug.Write(OptionMap.Instance.ToString()); Plug.Write("\nuciok"); Plug.Write(Constants.endl); } else if (token == "perft") { token = stack.Pop(); // Read depth var ss = Utils.CreateStack( string.Format( "{0} {1} {2} current perft", OptionMap.Instance["Hash"].v, OptionMap.Instance["Threads"].v, token)); Benchmark.benchmark(pos, ss); } else { Plug.Write("Unknown command: "); Plug.Write(cmd); Plug.Write(Constants.endl); } if (args.Length > 0) // Command line arguments have one-shot behaviour { Threads.wait_for_think_finished(); break; } } }
internal void do_move(Move m, StateInfo newSt, CheckInfo ci, bool moveIsCheck) { Debug.Assert(Utils.is_ok_M(m)); Debug.Assert(newSt != st); nodes++; Key k = st.key; newSt.pawnKey = st.pawnKey; newSt.materialKey = st.materialKey; newSt.npMaterialWHITE = st.npMaterialWHITE; newSt.npMaterialBLACK = st.npMaterialBLACK; newSt.castleRights = st.castleRights; newSt.rule50 = st.rule50; newSt.pliesFromNull = st.pliesFromNull; newSt.psqScore = st.psqScore; newSt.epSquare = st.epSquare; newSt.previous = st; st = newSt; // Update side to move k ^= zobSideToMove; // Increment the 50 moves rule draw counter. Resetting it to zero in the // case of a capture or a pawn move is taken care of later. st.rule50++; st.pliesFromNull++; if ((m & (3 << 14)) == (3 << 14)) { st.key = k; do_castle_move(true, m); return; } Color us = sideToMove; Color them = us ^ 1; Square from = ((m >> 6) & 0x3F); Square to = (m & 0x3F); Piece piece = board[from]; PieceType pt = (piece & 7); PieceType capture = ((m & (3 << 14)) == (2 << 14)) ? PieceTypeC.PAWN : (board[to] & 7); Debug.Assert(Utils.color_of(piece) == us); Debug.Assert(Utils.color_of(piece_on(to)) != us); Debug.Assert(capture != PieceTypeC.KING); if (capture != 0) { Square capsq = to; // If the captured piece is a pawn, update pawn hash key, otherwise // update non-pawn material. if (capture == PieceTypeC.PAWN) { if ((m & (3 << 14)) == (2 << 14)) { capsq += (them == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S); Debug.Assert(pt == PieceTypeC.PAWN); Debug.Assert(to == st.epSquare); Debug.Assert(Utils.relative_rank_CS(us, to) == RankC.RANK_6); Debug.Assert(piece_on(to) == PieceC.NO_PIECE); Debug.Assert(piece_on(capsq) == Utils.make_piece(them, PieceTypeC.PAWN)); board[capsq] = PieceC.NO_PIECE; } st.pawnKey ^= zobrist[them][PieceTypeC.PAWN][capsq]; } else { if (them == 0) { st.npMaterialWHITE -= PieceValueMidgame[capture]; } else { st.npMaterialBLACK -= PieceValueMidgame[capture]; } } // Remove the captured piece Bitboard capPieceMask = Utils.SquareBB[capsq]; occupied_squares ^= capPieceMask; byTypeBB[capture] ^= capPieceMask; byColorBB[them] ^= capPieceMask; // Update piece list, move the last piece at index[capsq] position and // shrink the list. // // WARNING: This is a not revresible operation. When we will reinsert the // captured piece in undo_move() we will put it at the end of the list and // not in its original place, it means index[] and pieceList[] are not // guaranteed to be invariant to a do_move() + undo_move() sequence. int[] plThemCapture = pieceList[them][capture]; int pcThemCapture = --pieceCount[them][capture]; Square lastSquare = plThemCapture[pcThemCapture]; index[lastSquare] = index[capsq]; plThemCapture[index[lastSquare]] = lastSquare; plThemCapture[pcThemCapture] = SquareC.SQ_NONE; // Update hash keys k ^= zobrist[them][capture][capsq]; st.materialKey ^= zobrist[them][capture][pcThemCapture]; // Update incremental scores st.psqScore -= pieceSquareTable[((them << 3) | capture)][capsq]; // Reset rule 50 counter st.rule50 = 0; } // Update hash key k ^= zobrist[us][pt][from] ^ zobrist[us][pt][to]; // Reset en passant square if (st.epSquare != SquareC.SQ_NONE) { k ^= zobEp[st.epSquare & 7]; st.epSquare = SquareC.SQ_NONE; } // Update castle rights if needed if ((st.castleRights != 0) && ((castleRightsMask[from] | castleRightsMask[to]) != 0)) { int cr = castleRightsMask[from] | castleRightsMask[to]; k ^= zobCastle[st.castleRights & cr]; st.castleRights &= ~cr; } // Move the piece Bitboard from_to_bb = Utils.SquareBB[from] ^ Utils.SquareBB[to]; occupied_squares ^= from_to_bb; byTypeBB[pt] ^= from_to_bb; byColorBB[us] ^= from_to_bb; board[to] = board[from]; board[from] = PieceC.NO_PIECE; // Update piece lists, index[from] is not updated and becomes stale. This // works as long as index[] is accessed just by known occupied squares. index[to] = index[from]; pieceList[us][pt][index[to]] = to; // If the moving piece is a pawn do some special extra work if (pt == PieceTypeC.PAWN) { // Set en-passant square, only if moved pawn can be captured if ((to ^ from) == 16 && ((((Utils.StepAttacksBB[((us << 3) | PieceTypeC.PAWN)][from + (us == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S)]) & (byTypeBB[PieceTypeC.PAWN] & byColorBB[them]))) != 0)) { st.epSquare = ((from + to) / 2); k ^= zobEp[st.epSquare & 7]; } if ((m & (3 << 14)) == (1 << 14)) { PieceType promotion = (((m >> 12) & 3) + 2); Debug.Assert(Utils.relative_rank_CS(us, to) == RankC.RANK_8); Debug.Assert(promotion >= PieceTypeC.KNIGHT && promotion <= PieceTypeC.QUEEN); // Replace the pawn with the promoted piece byTypeBB[PieceTypeC.PAWN] ^= Utils.SquareBB[to]; byTypeBB[promotion] |= Utils.SquareBB[to]; board[to] = ((us << 3) | promotion); // Update piece lists, move the last pawn at index[to] position // and shrink the list. Add a new promotion piece to the list. int[] plUsPawn = pieceList[us][PieceTypeC.PAWN]; Square lastSquare = plUsPawn[--pieceCount[us][PieceTypeC.PAWN]]; index[lastSquare] = index[to]; plUsPawn[index[lastSquare]] = lastSquare; plUsPawn[pieceCount[us][PieceTypeC.PAWN]] = SquareC.SQ_NONE; index[to] = pieceCount[us][promotion]; pieceList[us][promotion][index[to]] = to; // Update hash keys k ^= zobrist[us][PieceTypeC.PAWN][to] ^ zobrist[us][promotion][to]; st.pawnKey ^= zobrist[us][PieceTypeC.PAWN][to]; st.materialKey ^= zobrist[us][promotion][pieceCount[us][promotion]++] ^ zobrist[us][PieceTypeC.PAWN][pieceCount[us][PieceTypeC.PAWN]]; // Update incremental score st.psqScore += pieceSquareTable[((us << 3) | promotion)][to] - pieceSquareTable[((us << 3) | PieceTypeC.PAWN)][to]; // Update material if (us == 0) { st.npMaterialWHITE += PieceValueMidgame[promotion]; } else { st.npMaterialBLACK += PieceValueMidgame[promotion]; } } // Update pawn hash key st.pawnKey ^= zobrist[us][PieceTypeC.PAWN][from] ^ zobrist[us][PieceTypeC.PAWN][to]; // Reset rule 50 draw counter st.rule50 = 0; } // Update incremental scores st.psqScore += (pieceSquareTable[piece][to] - pieceSquareTable[piece][from]); // Set capture piece st.capturedType = capture; // Update the key with the final value st.key = k; // Update checkers bitboard, piece must be already moved st.checkersBB = 0; if (moveIsCheck) { if ((m & (3 << 14)) != 0) st.checkersBB = attackers_to(pieceList[them][PieceTypeC.KING][0]) & byColorBB[us]; else { // Direct checks if ((ci.checkSq[pt] & Utils.SquareBB[to]) != 0) { st.checkersBB |= Utils.SquareBB[to]; } // Discovery checks if ((ci.dcCandidates != 0) && ((ci.dcCandidates & Utils.SquareBB[from]) != 0)) { if (pt != PieceTypeC.ROOK) st.checkersBB |= attacks_from_ROOK(pieceList[them][PieceTypeC.KING][0]) & ((byTypeBB[PieceTypeC.ROOK] | byTypeBB[PieceTypeC.QUEEN]) & byColorBB[us]); if (pt != PieceTypeC.BISHOP) st.checkersBB |= attacks_from_BISHOP(pieceList[them][PieceTypeC.KING][0]) & ((byTypeBB[PieceTypeC.BISHOP] | byTypeBB[PieceTypeC.QUEEN]) & byColorBB[us]); } } } // Finish sideToMove = sideToMove ^ 1; Debug.Assert(pos_is_ok()); }
// Search::validmoves() will return the list of all valid moves for a 'square' in UCI notation - all valid moves for the piece occupying that square // The list will be empty if no square is given or there is no piece on that square or the piece have no possible moves internal static void validmoves(Position pos, Stack<string> stack) { if (stack.Count > 0) { var squareFromString = stack.Pop(); var st = new StateInfo(); var mlist = MListBroker.GetObject(); mlist.pos = 0; Movegen.generate_legal(pos, mlist.moves, ref mlist.pos); var firstOne = true; for (var i = 0; i < mlist.pos; ++i) { var ms = mlist.moves[i]; var m = ms.move; var from = ((m >> 6) & 0x3F); if (Utils.square_to_string(from) == squareFromString) { if (!firstOne) { Plug.Write(" "); } Plug.Write(Utils.move_to_uci(m, false)); firstOne = false; } } MListBroker.Free(); } Plug.Write(Constants.endl); }
/// undo_move() unmakes a move. When it returns, the position should /// be restored to exactly the same state as before the move was made. internal void undo_move(Move m) { Debug.Assert(Utils.is_ok_M(m)); sideToMove = sideToMove ^ 1; if ((m & (3 << 14)) == (3 << 14)) { do_castle_move(false, m); return; } Color us = sideToMove; Color them = us ^ 1; Square from = ((m >> 6) & 0x3F); Square to = (m & 0x3F); Piece piece = board[to]; PieceType pt = piece & 7; PieceType capture = st.capturedType; Debug.Assert(is_empty(from)); Debug.Assert(Utils.color_of(piece) == us); Debug.Assert(capture != PieceTypeC.KING); if ((m & (3 << 14)) == (1 << 14)) { PieceType promotion = (((m >> 12) & 3) + 2); Debug.Assert(promotion == pt); Debug.Assert(Utils.relative_rank_CS(us, to) == RankC.RANK_8); Debug.Assert(promotion >= PieceTypeC.KNIGHT && promotion <= PieceTypeC.QUEEN); // Replace the promoted piece with the pawn byTypeBB[promotion] ^= Utils.SquareBB[to]; byTypeBB[PieceTypeC.PAWN] |= Utils.SquareBB[to]; board[to] = ((us << 3) | PieceTypeC.PAWN); // Update piece lists, move the last promoted piece at index[to] position // and shrink the list. Add a new pawn to the list. Square lastSquare = pieceList[us][promotion][--pieceCount[us][promotion]]; index[lastSquare] = index[to]; int[] plUsPromotion = pieceList[us][promotion]; plUsPromotion[index[lastSquare]] = lastSquare; plUsPromotion[pieceCount[us][promotion]] = SquareC.SQ_NONE; index[to] = pieceCount[us][PieceTypeC.PAWN]++; pieceList[us][PieceTypeC.PAWN][index[to]] = to; pt = PieceTypeC.PAWN; } // Put the piece back at the source square Bitboard from_to_bb = Utils.SquareBB[from] ^ Utils.SquareBB[to]; occupied_squares ^= from_to_bb; byTypeBB[pt] ^= from_to_bb; byColorBB[us] ^= from_to_bb; board[from] = board[to]; board[to] = PieceC.NO_PIECE; // Update piece lists, index[to] is not updated and becomes stale. This // works as long as index[] is accessed just by known occupied squares. index[from] = index[to]; pieceList[us][pt][index[from]] = from; if (capture != 0) { Square capsq = to; if ((m & (3 << 14)) == (2 << 14)) { capsq -= (us == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S); Debug.Assert(pt == PieceTypeC.PAWN); Debug.Assert(to == st.previous.epSquare); Debug.Assert(Utils.relative_rank_CS(us, to) == RankC.RANK_6); Debug.Assert(piece_on(capsq) == PieceC.NO_PIECE); } // Restore the captured piece Bitboard capSqMask = Utils.SquareBB[capsq]; occupied_squares |= capSqMask; byTypeBB[capture] |= capSqMask; byColorBB[them] |= capSqMask; board[capsq] = ((them << 3) | capture); // Update piece list, add a new captured piece in capsq square index[capsq] = pieceCount[them][capture]++; pieceList[them][capture][index[capsq]] = capsq; } // Finally point our state pointer back to the previous state st = st.previous; Debug.Assert(pos_is_ok()); }
/// move_to_san() takes a position and a move as input, where it is assumed /// that the move is a legal move for the position. The return value is /// a string containing the move in short algebraic notation. internal static string move_to_san(Position pos, Move m) { if (m == MoveC.MOVE_NONE) return "(none)"; if (m == MoveC.MOVE_NULL) return "(null)"; Debug.Assert(is_ok_M(m)); Bitboard attackers; bool ambiguousMove, ambiguousFile, ambiguousRank; Square sq, from = from_sq(m); Square to = to_sq(m); PieceType pt = type_of(pos.piece_moved(m)); StringBuilder san = new StringBuilder(); if (is_castle(m)) san.Append((to_sq(m) < from_sq(m) ? "O-O-O" : "O-O")); else { if (pt != PieceTypeC.PAWN) { san.Append(piece_type_to_char(pt).ToString()); // Disambiguation if we have more then one piece with destination 'to' // note that for pawns is not needed because starting file is explicit. attackers = pos.attackers_to(to) & pos.pieces_PTC(pt, pos.sideToMove); xor_bit(ref attackers, from); ambiguousMove = ambiguousFile = ambiguousRank = false; while (attackers != 0) { sq = pop_1st_bit(ref attackers); // Pinned pieces are not included in the possible sub-set if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces())) continue; if (file_of(sq) == file_of(from)) ambiguousFile = true; if (rank_of(sq) == rank_of(from)) ambiguousRank = true; ambiguousMove = true; } if (ambiguousMove) { if (!ambiguousFile) san.Append(file_to_char(file_of(from))); else if (!ambiguousRank) san.Append(rank_to_char(rank_of(from))); else san.Append(square_to_string(from)); } } if (pos.is_capture(m)) { if (pt == PieceTypeC.PAWN) san.Append(file_to_char(file_of(from))); san.Append('x'); } san.Append(square_to_string(to)); if (is_promotion(m)) { san.Append('='); san.Append(piece_type_to_char(promotion_type(m))); } } CheckInfo ci = CheckInfoBroker.GetObject(); ci.CreateCheckInfo(pos); if (pos.move_gives_check(m, ci)) { StateInfo st = new StateInfo(); pos.do_move(m, st); MList mlist = MListBroker.GetObject(); mlist.pos = 0; Movegen.generate_legal(pos, mlist.moves, ref mlist.pos); san.Append(mlist.pos > 0 ? "+" : "#"); MListBroker.Free(); pos.undo_move(m); } CheckInfoBroker.Free(); return san.ToString(); }
/// do_castle_move() is a private method used to do/undo a castling /// move. Note that castling moves are encoded as "king captures friendly rook" /// moves, for instance white short castling in a non-Chess960 game is encoded /// as e1h1. internal void do_castle_move(bool Do, Move m) { Debug.Assert(Utils.is_ok_M(m)); Debug.Assert(Utils.is_castle(m)); Square kto, kfrom, rfrom, rto, kAfter, rAfter; Color us = sideToMove; Square kBefore = Utils.from_sq(m); Square rBefore = Utils.to_sq(m); // Find after-castle squares for king and rook if (rBefore > kBefore) // O-O { kAfter = Utils.relative_square(us, SquareC.SQ_G1); rAfter = Utils.relative_square(us, SquareC.SQ_F1); } else // O-O-O { kAfter = Utils.relative_square(us, SquareC.SQ_C1); rAfter = Utils.relative_square(us, SquareC.SQ_D1); } kfrom = Do ? kBefore : kAfter; rfrom = Do ? rBefore : rAfter; kto = Do ? kAfter : kBefore; rto = Do ? rAfter : rBefore; Debug.Assert(piece_on(kfrom) == Utils.make_piece(us, PieceTypeC.KING)); Debug.Assert(piece_on(rfrom) == Utils.make_piece(us, PieceTypeC.ROOK)); // Move the pieces, with some care; in chess960 could be kto == rfrom Bitboard k_from_to_bb = Utils.SquareBB[kfrom] ^ Utils.SquareBB[kto]; Bitboard r_from_to_bb = Utils.SquareBB[rfrom] ^ Utils.SquareBB[rto]; byTypeBB[PieceTypeC.KING] ^= k_from_to_bb; byTypeBB[PieceTypeC.ROOK] ^= r_from_to_bb; occupied_squares ^= k_from_to_bb ^ r_from_to_bb; byColorBB[us] ^= k_from_to_bb ^ r_from_to_bb; // Update board Piece king = Utils.make_piece(us, PieceTypeC.KING); Piece rook = Utils.make_piece(us, PieceTypeC.ROOK); board[kfrom] = board[rfrom] = PieceC.NO_PIECE; board[kto] = king; board[rto] = rook; // Update piece lists pieceList[us][PieceTypeC.KING][index[kfrom]] = kto; pieceList[us][PieceTypeC.ROOK][index[rfrom]] = rto; int tmp = index[rfrom]; // In Chess960 could be kto == rfrom index[kto] = index[kfrom]; index[rto] = tmp; if (Do) { // Reset capture field st.capturedType = PieceTypeC.NO_PIECE_TYPE; // Update incremental scores st.psqScore += psq_delta(king, kfrom, kto); st.psqScore += psq_delta(rook, rfrom, rto); // Update hash key st.key ^= zobrist[us][PieceTypeC.KING][kfrom] ^ zobrist[us][PieceTypeC.KING][kto]; st.key ^= zobrist[us][PieceTypeC.ROOK][rfrom] ^ zobrist[us][PieceTypeC.ROOK][rto]; // Clear en passant square if (st.epSquare != SquareC.SQ_NONE) { st.key ^= zobEp[Utils.file_of(st.epSquare)]; st.epSquare = SquareC.SQ_NONE; } // Update castling rights st.key ^= zobCastle[st.castleRights & castleRightsMask[kfrom]]; st.castleRights &= ~castleRightsMask[kfrom]; // Update checkers BB st.checkersBB = attackers_to(king_square(Utils.flip_C(us))) & pieces_C(us); // Finish sideToMove = Utils.flip_C(sideToMove); } else // Undo: point our state pointer back to the previous state st = st.previous; Debug.Assert(pos_is_ok()); }
/// move_to_san() takes a position and a legal Move as input and returns its /// short algebraic notation representation. internal static string move_to_san(Position pos, int m) { if (m == MoveC.MOVE_NONE) { return "(none)"; } if (m == MoveC.MOVE_NULL) { return "(null)"; } Debug.Assert(pos.move_is_legal(m)); Bitboard others, b; Color us = pos.sideToMove; var san = new StringBuilder(); Square from = from_sq(m); Square to = to_sq(m); Piece pc = pos.piece_on(from); PieceType pt = type_of(pc); if (type_of_move(m) == MoveTypeC.CASTLING) { san.Append(to > from ? "O-O" : "O-O-O"); } else { if (pt != PieceTypeC.PAWN) { san.Append(PieceToChar[ColorC.WHITE][pt]); // Upper case // Disambiguation if we have more then one piece of type 'pt' that can // reach 'to' with a legal move. others = b = (pos.attacks_from_PS(pc, to) & pos.pieces_PTC(pt, us)) ^ (ulong)from; while (others != 0) { Move move = make_move(pop_lsb(ref b), to); if (!pos.pl_move_is_legal(move, pos.pinned_pieces())) { others ^= (ulong)from_sq(move); } } if (others != 0) { if ((others & file_bb_S(from)) == 0) { san.Append(file_to_char(file_of(from))); } else if ((others & rank_bb_S(from)) == 0) { san.Append(rank_to_char(rank_of(from))); } else { san.Append(square_to_string(from)); } } } else if (pos.is_capture(m)) { san.Append(file_to_char(file_of(from))); } if (pos.is_capture(m)) { san.Append('x'); } san.Append(square_to_string(to)); if (type_of_move(m) == MoveTypeC.PROMOTION) { san.Append('='); san.Append(PieceToChar[ColorC.WHITE][promotion_type(m)]); } } var ci = CheckInfoBroker.GetObject(); ci.CreateCheckInfo(pos); if (pos.move_gives_check(m, ci)) { var st = new StateInfo(); pos.do_move(m, st); var mlist = MListBroker.GetObject(); mlist.pos = 0; Movegen.generate_legal(pos, mlist.moves, ref mlist.pos); san.Append(mlist.pos > 0 ? "+" : "#"); MListBroker.Free(); pos.undo_move(m); } CheckInfoBroker.Free(); return san.ToString(); }
/// do_null_move() is used to do/undo a "null move": It flips the side /// to move and updates the hash key without executing any move on the board. internal void do_null_move(bool Do, StateInfo backupSt) { Debug.Assert(!in_check()); // Back up the information necessary to undo the null move to the supplied // StateInfo object. Note that differently from normal case here backupSt // is actually used as a backup storage not as the new state. This reduces // the number of fields to be copied. StateInfo src = Do ? st : backupSt; StateInfo dst = Do ? backupSt : st; dst.key = src.key; dst.epSquare = src.epSquare; dst.psqScore = src.psqScore; dst.rule50 = src.rule50; dst.pliesFromNull = src.pliesFromNull; sideToMove = Utils.flip_C(sideToMove); if (Do) { if (st.epSquare != SquareC.SQ_NONE) st.key ^= zobEp[Utils.file_of(st.epSquare)]; st.key ^= zobSideToMove; st.epSquare = SquareC.SQ_NONE; st.rule50++; st.pliesFromNull = 0; } Debug.Assert(pos_is_ok()); }
internal static void init() { for (int i = 0; i < Constants.BROKER_SLOTS; i++) { _pool[i] = new StateInfo[0]; } }
public StateInfoArray() { for (int i = 0; i < (Constants.MAX_PLY_PLUS_2); i++) { state[i] = new StateInfo(); } }