inline void do_rjsmc ( vsmc::Sampler<T> &sampler, const std::string &, std::ostream &zconst_file, const std::string &, typename MoveType::alpha_type::value_type alpha_config, typename MoveType::proposal_type::value_type proposal_config, std::size_t repeat) { sampler.move(MoveType(alpha_config, proposal_config), false); std::vector<double> cn(MaxCompNum); std::vector<double> w(sampler.size()); for (std::size_t i = 0; i != cn.size(); ++i) cn[i] = 0; for (std::size_t i = 0; i != repeat; ++i) { if (repeat > 1) std::cout << "Run: " << i + 1 << std::endl; sampler.initialize(); while (sampler.particle().value().state(0, 0).alpha() < 1) sampler.iterate(); sampler.particle().weight_set().read_weight(&w[0]); for (typename vsmc::Sampler<T>::size_type j = 0; j != sampler.size(); ++j) cn[sampler.particle().value().state(j, 0).comp_num() - 1] += w[j]; for (std::size_t j = 0; j != cn.size(); ++j) zconst_file << cn[j] << ' '; zconst_file << std::endl; } }
inline void smc_do (vsmc::Sampler<T> &sampler, std::ostream &zconst_file, const std::string &schedule_name, typename MoveType::alpha_type::value_type alpha_config, std::size_t iter_num) { sampler.move(MoveType(alpha_config, &sampler), false); for (std::size_t i = 0; i != Repeat; ++i) { if (Repeat > 1) std::cout << "Run: " << i + 1 << std::endl; zconst_file << schedule_name << ' '; zconst_file << alpha_config << ' '; smc_do(sampler, zconst_file, schedule_name, SM, iter_num); smc_do(sampler, zconst_file, schedule_name, CM, iter_num); zconst_file << std::endl; } }
void UpdateHistory(POS *p, int last_move, int move, int depth, int ply) { // Don't update stuff used for move ordering if a move changes material balance if (p->pc[Tsq(move)] != NO_PC || IsProm(move) || MoveType(move) == EP_CAP) return; // Asserts suggested by Ferdinand Mosca because history[] would overflow // if p->pc[Fsq(move)] == NO_PC. If they ever fire, either move or board // data are corrupt. assert(p->pc[Fsq(move)] != NO_PC); // To detect no_pc assert(p->pc[Fsq(move)] <= NO_PC); // To detect beyond no_pc for deeper examination // Increment history counter history[p->pc[Fsq(move)]][Tsq(move)] += 2 * depth * depth; // Prevent history counters from growing too high if (history[p->pc[Fsq(move)]][Tsq(move)] > HIST_LIMIT) TrimHistory(); // Update refutation table, saving new move in the table indexed // by the coordinates of last move. last_move == 0 is a null move, // special case of last_move == -1 denotes situations when updating // refutation table is switched off: at root or in QuiesceFlee() if (last_move >= 0) refutation[Fsq(last_move)][Tsq(last_move)] = move; // Update killer moves, taking care that they are different if (move != killer[ply][0]) { killer[ply][1] = killer[ply][0]; killer[ply][0] = move; } }
int BadCapture(POS *p, int move) { int fsq = Fsq(move); int tsq = Tsq(move); // Captures that gain material or capture equal piece are good by definition if (tp_value[TpOnSq(p, tsq)] >= tp_value[TpOnSq(p, fsq)]) return 0; // Bishop takes knight and knight takes bishop are good irrespectively from // the way minor pieces' values are tuned if ((TpOnSq(p, fsq) == B) && (TpOnSq(p, tsq) == N)) return 0; if ((TpOnSq(p, fsq) == N) && (TpOnSq(p, tsq) == B)) return 0; // En passant captures are good by definition if (MoveType(move) == EP_CAP) return 0; // We have to evaluate this capture using expensive Static Exchange Evaluation return Swap(p, fsq, tsq) < 0; }
int sHistory::MoveChangesMaterialBalance(sPosition *p, int move) { if (p->pc[Tsq(move)] != NO_PC || IsProm(move) || MoveType(move) == EP_CAP) return 1; return 0; }
void POS::UndoMove(int move, UNDO *u) { int sd = Opp(side); int op = side; int fsq = Fsq(move); int tsq = Tsq(move); int ftp = Tp(pc[tsq]); // moving piece int ttp = u->ttp; castle_flags = u->castle_flags; ep_sq = u->ep_sq; rev_moves = u->rev_moves; pawn_key = u->pawn_key; hash_key = u->hash_key; head--; pc[fsq] = Pc(sd, ftp); pc[tsq] = NO_PC; cl_bb[sd] ^= SqBb(fsq) | SqBb(tsq); tp_bb[ftp] ^= SqBb(fsq) | SqBb(tsq); #ifndef LEAF_PST mg_pst[sd] += Param.mg_pst_data[sd][ftp][fsq] - Param.mg_pst_data[sd][ftp][tsq]; eg_pst[sd] += Param.eg_pst_data[sd][ftp][fsq] - Param.eg_pst_data[sd][ftp][tsq]; #endif // Update king location if (ftp == K) king_sq[sd] = fsq; // Undo capture if (ttp != NO_TP) { pc[tsq] = Pc(op, ttp); cl_bb[op] ^= SqBb(tsq); tp_bb[ttp] ^= SqBb(tsq); phase += phase_value[ttp]; #ifndef LEAF_PST mg_pst[op] += Param.mg_pst_data[op][ttp][tsq]; eg_pst[op] += Param.eg_pst_data[op][ttp][tsq]; #endif cnt[op][ttp]++; } switch (MoveType(move)) { case NORMAL: break; case CASTLE: // define complementary rook move switch (tsq) { case C1: { fsq = A1; tsq = D1; break; } case G1: { fsq = H1; tsq = F1; break; } case C8: { fsq = A8; tsq = D8; break; } case G8: { fsq = H8; tsq = F8; break; } } pc[tsq] = NO_PC; pc[fsq] = Pc(sd, R); cl_bb[sd] ^= SqBb(fsq) | SqBb(tsq); tp_bb[R] ^= SqBb(fsq) | SqBb(tsq); #ifndef LEAF_PST mg_pst[sd] += Param.mg_pst_data[sd][R][fsq] - Param.mg_pst_data[sd][R][tsq]; eg_pst[sd] += Param.eg_pst_data[sd][R][fsq] - Param.eg_pst_data[sd][R][tsq]; #endif break; case EP_CAP: tsq ^= 8; pc[tsq] = Pc(op, P); cl_bb[op] ^= SqBb(tsq); tp_bb[P] ^= SqBb(tsq); phase += phase_value[P]; #ifndef LEAF_PST mg_pst[op] += Param.mg_pst_data[op][P][tsq]; eg_pst[op] += Param.eg_pst_data[op][P][tsq]; #endif cnt[op][P]++; break; case EP_SET: break; case N_PROM: case B_PROM: case R_PROM: case Q_PROM: pc[fsq] = Pc(sd, P); tp_bb[P] ^= SqBb(fsq); tp_bb[ftp] ^= SqBb(fsq); phase += phase_value[P] - phase_value[ftp]; #ifndef LEAF_PST mg_pst[sd] += Param.mg_pst_data[sd][P][fsq] - Param.mg_pst_data[sd][ftp][fsq]; eg_pst[sd] += Param.eg_pst_data[sd][P][fsq] - Param.eg_pst_data[sd][ftp][fsq]; #endif cnt[sd][P]++; cnt[sd][ftp]--; break; } side ^= 1; }
/*-----------------------------------------------------------------------------*/ bool Notation::AddMove(const MoveKif& move) { const MoveKif* prev_move = this->getPrevMove(); const MoveKif& move_current = this->current_moves_->back(); if (move.number() != 0 && move.number() != (this->move_current_->number() + 1)) { // 番号が違う return false; } if (move_current.number() != this->number()) { // 指し手の末尾と番号が違う場合 ブランチとして作成する this->current_moves_ = this->move_current_->MakeBranch(this->current_moves_); } int number = this->move_current_->number(); // 要素追加 this->current_moves_->emplace_back(move); this->move_current_ = &this->current_moves_->back(); MoveKif& move_kif = this->current_moves_->back(); if (move_kif.number() == 0) { move_kif.set_number(number + 1); } move_kif.set_side(this->position_.side_to_move()); if ((move_kif.move_type() & MOVE_FLAG)) { if (move_kif.to() == move_current.to()) { // 同ほげをたてる move_kif.set_move_type(MoveType(move_kif.move_type() | MoveType::SAME_FLAG)); } Piece capture_piece = position_.GetPiece(move_kif.to()); if (capture_piece != NO_PIECE) { // 捕獲する場合 captureフラグを建てる move_kif.set_capture_piece(capture_piece); move_kif.set_move_type(MoveType(move_kif.move_type() | MoveType::CAPTURE_FLAG)); } } else if (is_result(move_kif.move_type())) { this->result_ = move_kif.move_type(); this->winner_ = this->getWinPlayer(move_kif); } if (move_kif.totalTime == 0) { // トータル時間の計算 move_kif.totalTime = ((prev_move != nullptr) ? prev_move->totalTime : 0) + move_kif.time; } if (is_move(move_kif.move_type())) { // 盤面をすすめる this->position_.DoMove(move_kif); } return true; }
void sManipulator::DoMove(sPosition *p, int move, UNDO *u) { int side = p->side; // moving side int fsq = Fsq(move); // start square int tsq = Tsq(move); // target square int ftp = TpOnSq(p, fsq); // moving piece int ttp = TpOnSq(p, tsq); // captured piece U64 bbMove = SqBb(fsq) | SqBb(tsq); // optimization from Stockfish // save data for undoing a move u->ttp = ttp; u->castleFlags = p->castleFlags; u->epSquare = p->epSquare; u->reversibleMoves = p->reversibleMoves; u->hashKey = p->hashKey; u->pawnKey = p->pawnKey; p->repetitionList[p->head++] = p->hashKey; // update reversible move counter (zeroing is done on captures and pawn moves) p->reversibleMoves++; p->hashKey ^= zobCastle[p->castleFlags]; p->castleFlags &= castleMask[fsq] & castleMask[tsq]; p->hashKey ^= zobCastle[p->castleFlags]; // clear en passant square if (p->epSquare != NO_SQ) { p->hashKey ^= zobEp[File(p->epSquare)]; p->epSquare = NO_SQ; } // move a piece from start square p->pc[fsq] = NO_PC; p->pc[tsq] = Pc(side, ftp); p->hashKey ^= zobPiece[Pc(side, ftp)][fsq] ^ zobPiece[Pc(side, ftp)][tsq]; if (ftp == P) { p->reversibleMoves = 0; p->pawnKey ^= zobPiece[Pc(side, ftp)][fsq] ^ zobPiece[Pc(side, ftp)][tsq]; } p->bbCl[side] ^= bbMove; p->bbTp[ftp] ^= bbMove; p->pstMg[side] += Data.pstMg[side][ftp][tsq] - Data.pstMg[side][ftp][fsq]; p->pstEg[side] += Data.pstEg[side][ftp][tsq] - Data.pstEg[side][ftp][fsq]; // on a king move update king location data if (ftp == K) p->kingSquare[side] = tsq; // capture if (ttp != NO_TP) { p->reversibleMoves = 0; p->hashKey ^= zobPiece[Pc(Opp(side), ttp)][tsq]; if (ttp == P) p->pawnKey ^= zobPiece[Pc(Opp(side), ttp)][tsq]; p->bbCl[Opp(side)] ^= SqBb(tsq); p->bbTp[ttp] ^= SqBb(tsq); p->pcCount[Opp(side)][ttp]--; p->pieceMat[Opp(side)] -= Data.matValue[ttp]; p->phase -= Data.phaseValue[ttp]; p->pstMg[Opp(side)] -= Data.pstMg[Opp(side)][ttp][tsq]; p->pstEg[Opp(side)] -= Data.pstEg[Opp(side)][ttp][tsq]; } switch (MoveType(move)) { case NORMAL: break; case CASTLE: if (tsq > fsq) { fsq += 3; tsq -= 1; } else { fsq -= 4; tsq += 1; } p->pc[fsq] = NO_PC; p->pc[tsq] = Pc(side, R); p->hashKey ^= zobPiece[Pc(side, R)][fsq] ^ zobPiece[Pc(side, R)][tsq]; p->bbCl[side] ^= SqBb(fsq) | SqBb(tsq); p->bbTp[R] ^= SqBb(fsq) | SqBb(tsq); p->pstMg[side] += Data.pstMg[side][R][tsq] - Data.pstMg[side][R][fsq]; p->pstEg[side] += Data.pstEg[side][R][tsq] - Data.pstEg[side][R][fsq]; break; case EP_CAP: tsq ^= 8; p->pc[tsq] = NO_PC; p->hashKey ^= zobPiece[Pc(Opp(side), P)][tsq]; p->pawnKey ^= zobPiece[Pc(Opp(side), P)][tsq]; p->bbCl[Opp(side)] ^= SqBb(tsq); p->bbTp[P] ^= SqBb(tsq); p->pcCount[Opp(side)][P]--; p->phase -= Data.phaseValue[P]; p->pstMg[Opp(side)] -= Data.pstMg[Opp(side)][P][tsq]; p->pstEg[Opp(side)] -= Data.pstEg[Opp(side)][P][tsq]; break; case EP_SET: tsq ^= 8; if (bbPawnAttacks[side][tsq] & bbPc(p, Opp(side), P)) { p->epSquare = tsq; p->hashKey ^= zobEp[File(tsq)]; } break; // promotion: (1) add promoted piece and add values associated with it case N_PROM: // (2) remove promoted pawn and substract values associated with it case B_PROM: case R_PROM: case Q_PROM: ftp = PromType(move); p->pc[tsq] = Pc(side, ftp); p->hashKey ^= zobPiece[Pc(side, P)][tsq] ^ zobPiece[Pc(side, ftp)][tsq]; p->pawnKey ^= zobPiece[Pc(side, P)][tsq]; p->bbTp[P] ^= SqBb(tsq); p->bbTp[ftp]^= SqBb(tsq); p->pcCount[side][ftp]++; p->pcCount[side][P]--; p->pieceMat[side] += Data.matValue[ftp]; p->phase += Data.phaseValue[ftp] - Data.phaseValue[P]; p->pstMg[side] += Data.pstMg[side][ftp][tsq] - Data.pstMg[side][P][tsq]; p->pstEg[side] += Data.pstEg[side][ftp][tsq] - Data.pstEg[side][P][tsq]; break; } p->side ^= 1; p->hashKey ^= SIDE_RANDOM; }
int Search(POS *p, int ply, int alpha, int beta, int depth, int was_null, int last_move, int last_capt_sq, int *pv) { int best, score, null_score, move, new_depth, new_pv[MAX_PLY]; int fl_check, fl_prunable_node, fl_prunable_move, mv_type, reduction; int is_pv = (beta > alpha + 1); int mv_tried = 0, quiet_tried = 0, fl_futility = 0; int mv_played[MAX_MOVES]; int mv_hist_score; int victim, last_capt; MOVES m[1]; UNDO u[1]; assert(ply > 0); // Quiescence search entry point if (depth <= 0) return QuiesceChecks(p, ply, alpha, beta, pv); // Periodically check for timeout, ponderhit or stop command nodes++; CheckTimeout(); // Quick exit on a timeout or on a statically detected draw if (abort_search) return 0; if (ply) *pv = 0; if (IsDraw(p)) return DrawScore(p); // Mate distance pruning int checkmatingScore = MATE - ply; if (checkmatingScore < beta) { beta = checkmatingScore; if (alpha >= checkmatingScore) return alpha; } int checkmatedScore = -MATE + ply; if (checkmatedScore > alpha) { alpha = checkmatedScore; if (beta <= checkmatedScore) return beta; } // Retrieving data from transposition table. We hope for a cutoff // or at least for a move to improve move ordering. move = 0; if (TransRetrieve(p->hash_key, &move, &score, alpha, beta, depth, ply)) { // For move ordering purposes, a cutoff from hash is treated // exactly like a cutoff from search if (score >= beta) UpdateHistory(p, last_move, move, depth, ply); // In pv nodes only exact scores are returned. This is done because // there is much more pruning and reductions in zero-window nodes, // so retrieving such scores in pv nodes works like retrieving scores // from slightly lower depth. if (!is_pv || (score > alpha && score < beta)) return score; } // Safeguard against exceeding ply limit if (ply >= MAX_PLY - 1) return Eval.Return(p, 1); // Are we in check? Knowing that is useful when it comes // to pruning/reduction decisions fl_check = InCheck(p); // INTERNAL ITERATIVE DEEPENING - we try to get a hash move to improve move ordering if (!move && is_pv && depth >= 6 && !fl_check) { Search(p, ply, alpha, beta, depth - 2, 0, 0, -1, new_pv); if (abort_search) return 0; TransRetrieve(p->hash_key, &move, &score, alpha, beta, depth, ply); } // Can we prune this node? fl_prunable_node = !fl_check && !is_pv && alpha > -MAX_EVAL && beta < MAX_EVAL; // Beta pruning / static null move if (use_beta_pruning && fl_prunable_node && depth <= 3 && !was_null) { int sc = Eval.Return(p, 1) - 120 * depth; // TODO: Tune me! if (sc > beta) return sc; } // Null move if (use_nullmove && fl_prunable_node && depth > 1 && !was_null && MayNull(p) ) { int eval = Eval.Return(p, 1); if (eval > beta) { new_depth = depth - ((823 + 67 * depth) / 256); // simplified Stockfish formula // omit null move search if normal search to the same depth wouldn't exceed beta // (sometimes we can check it for free via hash table) if (TransRetrieve(p->hash_key, &move, &null_score, alpha, beta, new_depth, ply)) { if (null_score < beta) goto avoid_null; } p->DoNull(u); if (new_depth > 0) score = -Search(p, ply + 1, -beta, -beta + 1, new_depth, 1, 0, -1, new_pv); else score = -QuiesceChecks(p, ply + 1, -beta, -beta + 1, new_pv); p->UndoNull(u); // Verification search (nb. immediate null move within it is prohibited) if (new_depth > 6 && score >= beta && use_null_verification) score = Search(p, ply, alpha, beta, new_depth - 5, 1, move, -1, new_pv); if (abort_search ) return 0; if (score >= beta) return score; } } avoid_null: // end of null move code // Razoring based on Toga II 3.0 if (use_razoring && fl_prunable_node && !move && !was_null && !(p->Pawns(p->side) & bbRelRank[p->side][RANK_7]) // no pawns to promote in one move && depth <= 3) { int threshold = beta - razor_margin[depth]; int eval = Eval.Return(p, 1); if (eval < threshold) { score = QuiesceChecks(p, ply, alpha, beta, pv); if (score < threshold) return score; } } // end of razoring code // Init moves and variables before entering main loop best = -INF; InitMoves(p, m, move, Refutation(last_move), ply); // Main loop while ((move = NextMove(m, &mv_type))) { // Gather data about the move mv_hist_score = history[p->pc[Fsq(move)]][Tsq(move)]; victim = TpOnSq(p, Tsq(move)); if (victim != NO_TP) last_capt = Tsq(move); else last_capt = -1; // Set futility pruning flag before the first applicable move is tried if (mv_type == MV_NORMAL && quiet_tried == 0) { if (use_futility && fl_prunable_node && depth <= 6) { if (Eval.Return(p, 1) + fut_margin[depth] < beta) fl_futility = 1; } } p->DoMove(move, u); if (Illegal(p)) { p->UndoMove(move, u); continue; } // Update move statistics // (needed for reduction/pruning decisions and for updating history score) mv_played[mv_tried] = move; mv_tried++; if (mv_type == MV_NORMAL) quiet_tried++; // Can we prune this move? fl_prunable_move = !InCheck(p) && (mv_type == MV_NORMAL) && (mv_hist_score < hist_limit); // Set new search depth new_depth = depth - 1; // Check extension (pv node or low depth) if (is_pv || depth < 9) { new_depth += InCheck(p); if (is_pv && Tsq(move) == last_capt_sq) new_depth += 1; } // Futility pruning if (fl_futility && fl_prunable_move && mv_tried > 1) { p->UndoMove(move, u); continue; } // Late move pruning if (use_lmp && fl_prunable_node && fl_prunable_move && quiet_tried > lmp_limit[depth] && depth <= 3 && MoveType(move) != CASTLE ) { p->UndoMove(move, u); continue; } // Late move reduction reduction = 0; if (use_lmr && depth >= 2 && mv_tried > 3 && alpha > -MAX_EVAL && beta < MAX_EVAL && !fl_check && fl_prunable_move && lmr_size[is_pv][depth][mv_tried] > 0 && MoveType(move) != CASTLE ) { // read reduction size from the table reduction = lmr_size[is_pv][depth][mv_tried]; // increase reduction on bad history score if (mv_hist_score < 0 && new_depth - reduction > 2 && lmr_hist_adjustement) reduction++; // reduce search depth new_depth -= reduction; } // a place to come back if reduction looks suspect re_search: // PVS if (best == -INF) score = -Search(p, ply + 1, -beta, -alpha, new_depth, 0, move, last_capt, new_pv); else { score = -Search(p, ply + 1, -alpha - 1, -alpha, new_depth, 0, move, last_capt, new_pv); if (!abort_search && score > alpha && score < beta) score = -Search(p, ply + 1, -beta, -alpha, new_depth, 0, move, last_capt, new_pv); } // Reduced move scored above alpha - we need to re-search it if (reduction && score > alpha) { new_depth += reduction; reduction = 0; goto re_search; } // Undo move p->UndoMove(move, u); if (abort_search) return 0; // Beta cutoff if (score >= beta) { if (!fl_check) { UpdateHistory(p, last_move, move, depth, ply); for (int mv = 0; mv < mv_tried; mv++) DecreaseHistory(p, mv_played[mv], depth); } TransStore(p->hash_key, move, score, LOWER, depth, ply); return score; } // Updating score and alpha if (score > best) { best = score; if (score > alpha) { alpha = score; BuildPv(pv, new_pv, move); } } } // end of the main loop // Return correct checkmate/stalemate score if (best == -INF) return InCheck(p) ? -MATE + ply : DrawScore(p); // Save score in the transposition table if (*pv) { if (!fl_check) { UpdateHistory(p, last_move, *pv, depth, ply); for (int mv = 0; mv < mv_tried; mv++) DecreaseHistory(p, mv_played[mv], depth); } TransStore(p->hash_key, *pv, best, EXACT, depth, ply); } else TransStore(p->hash_key, 0, best, UPPER, depth, ply); return best; }
int SearchRoot(POS *p, int ply, int alpha, int beta, int depth, int *pv) { int best, score, move, new_depth, new_pv[MAX_PLY]; int fl_check, fl_prunable_move, mv_type, reduction; int mv_tried = 0, quiet_tried = 0; int mv_played[MAX_MOVES]; int mv_hist_score; int victim, last_capt; int move_change = 0; fl_has_choice = 0; MOVES m[1]; UNDO u[1]; // Periodically check for timeout, ponderhit or stop command nodes++; CheckTimeout(); // Quick exit if (abort_search) return 0; // Retrieving data from transposition table. We hope for a cutoff // or at least for a move to improve move ordering. move = 0; if (TransRetrieve(p->hash_key, &move, &score, alpha, beta, depth, ply)) { // For move ordering purposes, a cutoff from hash is treated // exactly like a cutoff from search if (score >= beta) UpdateHistory(p, -1, move, depth, ply); // Root node is a pv node, so we return only exact scores if (score > alpha && score < beta) return score; } // Are we in check? Knowing that is useful when it comes // to pruning/reduction decisions fl_check = InCheck(p); // Init moves and variables before entering main loop best = -INF; InitMoves(p, m, move, Refutation(-1), ply); // Main loop while ((move = NextMove(m, &mv_type))) { mv_hist_score = history[p->pc[Fsq(move)]][Tsq(move)]; victim = TpOnSq(p, Tsq(move)); if (victim != NO_TP) last_capt = Tsq(move); else last_capt = -1; p->DoMove(move, u); if (Illegal(p)) { p->UndoMove(move, u); continue; } // Update move statistics (needed for reduction/pruning decisions) mv_played[mv_tried] = move; mv_tried++; if (mv_tried > 1) fl_has_choice = 1; // we have a choice between at least two root moves if (depth > 16 && verbose) DisplayCurrmove(move, mv_tried); if (mv_type == MV_NORMAL) quiet_tried++; fl_prunable_move = !InCheck(p) && (mv_type == MV_NORMAL); // Set new search depth new_depth = depth - 1 + InCheck(p); // Late move reduction reduction = 0; if (use_lmr && depth >= 2 && mv_tried > 3 && mv_hist_score < hist_limit && alpha > -MAX_EVAL && beta < MAX_EVAL && !fl_check && fl_prunable_move && lmr_size[1][depth][mv_tried] > 0 && MoveType(move) != CASTLE ) { reduction = lmr_size[1][depth][mv_tried]; // increase reduction on bad history score if (mv_hist_score < 0 && new_depth - reduction > 2 && lmr_hist_adjustement) reduction++; new_depth -= reduction; } re_search: // PVS if (best == -INF) score = -Search(p, ply + 1, -beta, -alpha, new_depth, 0, move, last_capt, new_pv); else { score = -Search(p, ply + 1, -alpha - 1, -alpha, new_depth, 0, move, last_capt, new_pv); if (!abort_search && score > alpha && score < beta) score = -Search(p, ply + 1, -beta, -alpha, new_depth, 0, move, last_capt, new_pv); } // Reduced move scored above alpha - we need to re-search it if (reduction && score > alpha) { new_depth += reduction; reduction = 0; goto re_search; } p->UndoMove(move, u); if (abort_search) return 0; // Beta cutoff if (score >= beta) { if (!fl_check) { UpdateHistory(p, -1, move, depth, ply); for (int mv = 0; mv < mv_tried; mv++) DecreaseHistory(p, mv_played[mv], depth); } TransStore(p->hash_key, move, score, LOWER, depth, ply); // Update search time depending on whether the first move has changed if (depth > 4) { if (pv[0] != move) Timer.OnNewRootMove(); else Timer.OnOldRootMove(); } // Change the best move and show the new pv BuildPv(pv, new_pv, move); DisplayPv(score, pv); return score; } // Updating score and alpha if (score > best) { best = score; if (score > alpha) { alpha = score; // Update search time depending on whether the first move has changed if (depth > 4) { if (pv[0] != move) Timer.OnNewRootMove(); else Timer.OnOldRootMove(); } // Change the best move and show the new pv BuildPv(pv, new_pv, move); DisplayPv(score, pv); } } } // end of the main loop // Return correct checkmate/stalemate score if (best == -INF) return InCheck(p) ? -MATE + ply : DrawScore(p); // Save score in the transposition table if (*pv) { if (!fl_check) { UpdateHistory(p, -1, *pv, depth, ply); for (int mv = 0; mv < mv_tried; mv++) DecreaseHistory(p, mv_played[mv], depth); } TransStore(p->hash_key, *pv, best, EXACT, depth, ply); } else TransStore(p->hash_key, 0, best, UPPER, depth, ply); return best; }