mirror of
https://github.com/sockspls/badfish
synced 2025-04-29 16:23:09 +00:00
parent
74e2fa97b7
commit
89723339d9
12 changed files with 103 additions and 102 deletions
|
@ -111,4 +111,4 @@ to where the source code can be found. If you make any changes to the
|
|||
source code, these changes must also be made available under the GPL.
|
||||
|
||||
For full details, read the copy of the GPL found in the file named
|
||||
*Copying.txt*
|
||||
*Copying.txt*.
|
||||
|
|
|
@ -166,7 +166,7 @@ void benchmark(const Position& current, istream& is) {
|
|||
|
||||
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
|
||||
|
||||
dbg_print(); // Just before to exit
|
||||
dbg_print(); // Just before exiting
|
||||
|
||||
cerr << "\n==========================="
|
||||
<< "\nTotal time (ms) : " << elapsed
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace {
|
|||
|
||||
namespace Trace {
|
||||
|
||||
enum Term { // First 8 entries are for PieceType
|
||||
enum Term { // The first 8 entries are for PieceType
|
||||
MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERM_NB
|
||||
};
|
||||
|
||||
|
@ -89,7 +89,7 @@ namespace {
|
|||
// which attack a square in the kingRing of the enemy king.
|
||||
int kingAttackersCount[COLOR_NB];
|
||||
|
||||
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
|
||||
// kingAttackersWeight[color] is the sum of the "weights" of the pieces of the
|
||||
// given color which attack a square in the kingRing of the enemy king. The
|
||||
// weights of the individual piece types are given by the elements in the
|
||||
// KingAttackWeights array.
|
||||
|
@ -161,20 +161,20 @@ namespace {
|
|||
const Score RookOnFile[2] = { S(19, 10), S(43, 21) };
|
||||
|
||||
// ThreatBySafePawn[PieceType] contains bonuses according to which piece
|
||||
// type is attacked by a pawn which is protected or not attacked.
|
||||
// type is attacked by a pawn which is protected or is not attacked.
|
||||
const Score ThreatBySafePawn[PIECE_TYPE_NB] = {
|
||||
S(0, 0), S(0, 0), S(176, 139), S(131, 127), S(217, 218), S(203, 215) };
|
||||
|
||||
|
||||
// Threat[by minor/by rook][attacked PieceType] contains
|
||||
// bonuses according to which piece type attacks which one.
|
||||
// Attacks on lesser pieces which are pawn defended are not considered.
|
||||
// Attacks on lesser pieces which are pawn-defended are not considered.
|
||||
const Score Threat[][PIECE_TYPE_NB] = {
|
||||
{ S(0, 0), S(0, 33), S(45, 43), S(46, 47), S(72,107), S(48,118) }, // by Minor
|
||||
{ S(0, 0), S(0, 25), S(40, 62), S(40, 59), S( 0, 34), S(35, 48) } // by Rook
|
||||
};
|
||||
|
||||
// ThreatByKing[on one/on many] contains bonuses for King attacks on
|
||||
// pawns or pieces which are not pawn defended.
|
||||
// pawns or pieces which are not pawn-defended.
|
||||
const Score ThreatByKing[2] = { S(3, 62), S(9, 138) };
|
||||
|
||||
// Passed[mg/eg][Rank] contains midgame and endgame bonuses for passed pawns.
|
||||
|
@ -226,7 +226,7 @@ namespace {
|
|||
const int KnightCheck = 14;
|
||||
|
||||
|
||||
// eval_init() initializes king and attack bitboards for given color
|
||||
// eval_init() initializes king and attack bitboards for a given color
|
||||
// adding pawn attacks. To be done at the beginning of the evaluation.
|
||||
|
||||
template<Color Us>
|
||||
|
@ -319,7 +319,7 @@ namespace {
|
|||
&& (pos.pieces(PAWN) & (s + pawn_push(Us))))
|
||||
score += MinorBehindPawn;
|
||||
|
||||
// Penalty for pawns on same color square of bishop
|
||||
// Penalty for pawns on the same color square as the bishop
|
||||
if (Pt == BISHOP)
|
||||
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
|
||||
|
||||
|
@ -352,7 +352,7 @@ namespace {
|
|||
if (ei.pi->semiopen_file(Us, file_of(s)))
|
||||
score += RookOnFile[!!ei.pi->semiopen_file(Them, file_of(s))];
|
||||
|
||||
// Penalize when trapped by the king, even more if king cannot castle
|
||||
// Penalize when trapped by the king, even more if the king cannot castle
|
||||
else if (mob <= 3)
|
||||
{
|
||||
Square ksq = pos.square<KING>(Us);
|
||||
|
@ -368,7 +368,7 @@ namespace {
|
|||
if (DoTrace)
|
||||
Trace::add(Pt, Us, score);
|
||||
|
||||
// Recursively call evaluate_pieces() of next piece type until KING excluded
|
||||
// Recursively call evaluate_pieces() of next piece type until KING is excluded
|
||||
return score - evaluate_pieces<DoTrace, Them, NextPt>(pos, ei, mobility, mobilityArea);
|
||||
}
|
||||
|
||||
|
@ -468,7 +468,7 @@ namespace {
|
|||
}
|
||||
|
||||
// Finally, extract the king danger score from the KingDanger[]
|
||||
// array and subtract the score from evaluation.
|
||||
// array and subtract the score from the evaluation.
|
||||
score -= KingDanger[std::max(std::min(attackUnits, 399), 0)];
|
||||
}
|
||||
|
||||
|
@ -479,8 +479,8 @@ namespace {
|
|||
}
|
||||
|
||||
|
||||
// evaluate_threats() assigns bonuses according to the type of attacking piece
|
||||
// and the type of attacked one.
|
||||
// evaluate_threats() assigns bonuses according to the types of the attacking
|
||||
// and the attacked pieces.
|
||||
|
||||
template<Color Us, bool DoTrace>
|
||||
Score evaluate_threats(const Position& pos, const EvalInfo& ei) {
|
||||
|
@ -619,7 +619,7 @@ namespace {
|
|||
// assign a smaller bonus if the block square isn't attacked.
|
||||
int k = !unsafeSquares ? 18 : !(unsafeSquares & blockSq) ? 8 : 0;
|
||||
|
||||
// If the path to queen is fully defended, assign a big bonus.
|
||||
// If the path to the queen is fully defended, assign a big bonus.
|
||||
// Otherwise assign a smaller bonus if the block square is defended.
|
||||
if (defendedSquares == squaresToQueen)
|
||||
k += 6;
|
||||
|
@ -687,7 +687,7 @@ namespace {
|
|||
|
||||
|
||||
// evaluate_initiative() computes the initiative correction value for the
|
||||
// position, i.e. second order bonus/malus based on the known attacking/defending
|
||||
// position, i.e., second order bonus/malus based on the known attacking/defending
|
||||
// status of the players.
|
||||
Score evaluate_initiative(const Position& pos, int asymmetry, Value eg) {
|
||||
|
||||
|
@ -720,7 +720,7 @@ namespace {
|
|||
if (pos.opposite_bishops())
|
||||
{
|
||||
// Endgame with opposite-colored bishops and no other pieces (ignoring pawns)
|
||||
// is almost a draw, in case of KBP vs KB is even more a draw.
|
||||
// is almost a draw, in case of KBP vs KB, it is even more a draw.
|
||||
if ( pos.non_pawn_material(WHITE) == BishopValueMg
|
||||
&& pos.non_pawn_material(BLACK) == BishopValueMg)
|
||||
sf = more_than_one(pos.pieces(PAWN)) ? ScaleFactor(31) : ScaleFactor(9);
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace {
|
|||
|
||||
// pick_best() finds the best move in the range (begin, end) and moves it to
|
||||
// the front. It's faster than sorting all the moves in advance when there
|
||||
// are few moves e.g. the possible captures.
|
||||
// are few moves, e.g., the possible captures.
|
||||
Move pick_best(ExtMove* begin, ExtMove* end)
|
||||
{
|
||||
std::swap(*begin, *std::max_element(begin, end));
|
||||
|
@ -64,12 +64,12 @@ namespace {
|
|||
/// Constructors of the MovePicker class. As arguments we pass information
|
||||
/// to help it to return the (presumably) good moves first, to decide which
|
||||
/// moves to return (in the quiescence search, for instance, we only want to
|
||||
/// search captures, promotions and some checks) and how important good move
|
||||
/// search captures, promotions, and some checks) and how important good move
|
||||
/// ordering is at the current node.
|
||||
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
|
||||
const CounterMovesStats& cmh, Move cm, Search::Stack* s)
|
||||
: pos(p), history(h), counterMovesHistory(&cmh), ss(s), countermove(cm), depth(d) {
|
||||
const CounterMoveStats& cmh, Move cm, Search::Stack* s)
|
||||
: pos(p), history(h), counterMoveHistory(&cmh), ss(s), countermove(cm), depth(d) {
|
||||
|
||||
assert(d > DEPTH_ZERO);
|
||||
|
||||
|
@ -80,7 +80,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
|
|||
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
|
||||
const HistoryStats& h, Square s)
|
||||
: pos(p), history(h), counterMovesHistory(nullptr) {
|
||||
: pos(p), history(h), counterMoveHistory(nullptr) {
|
||||
|
||||
assert(d <= DEPTH_ZERO);
|
||||
|
||||
|
@ -105,7 +105,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
|
|||
}
|
||||
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, Value th)
|
||||
: pos(p), history(h), counterMovesHistory(nullptr), threshold(th) {
|
||||
: pos(p), history(h), counterMoveHistory(nullptr), threshold(th) {
|
||||
|
||||
assert(!pos.checkers());
|
||||
|
||||
|
@ -127,9 +127,9 @@ template<>
|
|||
void MovePicker::score<CAPTURES>() {
|
||||
// Winning and equal captures in the main search are ordered by MVV, preferring
|
||||
// captures near our home rank. Surprisingly, this appears to perform slightly
|
||||
// better than SEE based move ordering: exchanging big pieces before capturing
|
||||
// better than SEE-based move ordering: exchanging big pieces before capturing
|
||||
// a hanging piece probably helps to reduce the subtree size.
|
||||
// In main search we want to push captures with negative SEE values to the
|
||||
// In the main search we want to push captures with negative SEE values to the
|
||||
// badCaptures[] array, but instead of doing it now we delay until the move
|
||||
// has been picked up, saving some SEE calls in case we get a cutoff.
|
||||
for (auto& m : *this)
|
||||
|
@ -142,14 +142,14 @@ void MovePicker::score<QUIETS>() {
|
|||
|
||||
for (auto& m : *this)
|
||||
m.value = history[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*counterMovesHistory)[pos.moved_piece(m)][to_sq(m)];
|
||||
+ (*counterMoveHistory)[pos.moved_piece(m)][to_sq(m)];
|
||||
}
|
||||
|
||||
template<>
|
||||
void MovePicker::score<EVASIONS>() {
|
||||
// Try winning and equal captures captures ordered by MVV/LVA, then non-captures
|
||||
// ordered by history value, then bad-captures and quiet moves with a negative
|
||||
// SEE ordered by SEE value.
|
||||
// Try winning and equal captures ordered by MVV/LVA, then non-captures ordered
|
||||
// by history value, then bad captures and quiet moves with a negative SEE ordered
|
||||
// by SEE value.
|
||||
Value see;
|
||||
|
||||
for (auto& m : *this)
|
||||
|
@ -164,7 +164,7 @@ void MovePicker::score<EVASIONS>() {
|
|||
}
|
||||
|
||||
|
||||
/// generate_next_stage() generates, scores and sorts the next bunch of moves,
|
||||
/// generate_next_stage() generates, scores, and sorts the next bunch of moves
|
||||
/// when there are no more moves to try for the current stage.
|
||||
|
||||
void MovePicker::generate_next_stage() {
|
||||
|
|
|
@ -65,10 +65,10 @@ private:
|
|||
T table[PIECE_NB][SQUARE_NB];
|
||||
};
|
||||
|
||||
typedef Stats<Move> MovesStats;
|
||||
typedef Stats<Move> MoveStats;
|
||||
typedef Stats<Value, false> HistoryStats;
|
||||
typedef Stats<Value, true> CounterMovesStats;
|
||||
typedef Stats<CounterMovesStats> CounterMovesHistoryStats;
|
||||
typedef Stats<Value, true> CounterMoveStats;
|
||||
typedef Stats<CounterMoveStats> CounterMoveHistoryStats;
|
||||
|
||||
|
||||
/// MovePicker class is used to pick one pseudo legal move at a time from the
|
||||
|
@ -85,7 +85,7 @@ public:
|
|||
|
||||
MovePicker(const Position&, Move, Depth, const HistoryStats&, Square);
|
||||
MovePicker(const Position&, Move, const HistoryStats&, Value);
|
||||
MovePicker(const Position&, Move, Depth, const HistoryStats&, const CounterMovesStats&, Move, Search::Stack*);
|
||||
MovePicker(const Position&, Move, Depth, const HistoryStats&, const CounterMoveStats&, Move, Search::Stack*);
|
||||
|
||||
Move next_move();
|
||||
|
||||
|
@ -97,7 +97,7 @@ private:
|
|||
|
||||
const Position& pos;
|
||||
const HistoryStats& history;
|
||||
const CounterMovesStats* counterMovesHistory;
|
||||
const CounterMoveStats* counterMoveHistory;
|
||||
Search::Stack* ss;
|
||||
Move countermove;
|
||||
Depth depth;
|
||||
|
|
|
@ -41,14 +41,14 @@ namespace {
|
|||
|
||||
// Backward pawn penalty by opposed flag
|
||||
const Score Backward[2] = { S(67, 42), S(49, 24) };
|
||||
|
||||
|
||||
// Unsupported pawn penalty for pawns which are neither isolated or backward,
|
||||
// by number of pawns it supports [less than 2 / exactly 2].
|
||||
const Score Unsupported[2] = { S(20, 10), S(25, 15) };
|
||||
|
||||
// Connected pawn bonus by opposed, phalanx, twice supported and rank
|
||||
Score Connected[2][2][2][RANK_NB];
|
||||
|
||||
|
||||
// Doubled pawn penalty by file
|
||||
const Score Doubled[FILE_NB] = {
|
||||
S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
||||
|
|
|
@ -61,7 +61,7 @@ using namespace Search;
|
|||
|
||||
namespace {
|
||||
|
||||
// Different node types, used as template parameter
|
||||
// Different node types, used as a template parameter
|
||||
enum NodeType { Root, PV, NonPV };
|
||||
|
||||
// Razoring and futility margin based on depth
|
||||
|
@ -76,7 +76,7 @@ namespace {
|
|||
return Reductions[PvNode][i][std::min(d, 63 * ONE_PLY)][std::min(mn, 63)];
|
||||
}
|
||||
|
||||
// Skill struct is used to implement strength limiting
|
||||
// Skill structure is used to implement strength limit
|
||||
struct Skill {
|
||||
Skill(int l) : level(l) {}
|
||||
bool enabled() const { return level < 20; }
|
||||
|
@ -88,8 +88,8 @@ namespace {
|
|||
Move best = MOVE_NONE;
|
||||
};
|
||||
|
||||
// EasyMoveManager struct is used to detect a so called 'easy move'; when PV is
|
||||
// stable across multiple search iterations we can fast return the best move.
|
||||
// EasyMoveManager structure is used to detect an 'easy move'. When the PV is
|
||||
// stable across multiple search iterations, we can quickly return the best move.
|
||||
struct EasyMoveManager {
|
||||
|
||||
void clear() {
|
||||
|
@ -106,7 +106,7 @@ namespace {
|
|||
|
||||
assert(newPv.size() >= 3);
|
||||
|
||||
// Keep track of how many times in a row 3rd ply remains stable
|
||||
// Keep track of how many times in a row the 3rd ply remains stable
|
||||
stableCnt = (newPv[2] == pv[2]) ? stableCnt + 1 : 0;
|
||||
|
||||
if (!std::equal(newPv.begin(), newPv.begin() + 3, pv))
|
||||
|
@ -129,7 +129,7 @@ namespace {
|
|||
|
||||
EasyMoveManager EasyMove;
|
||||
Value DrawValue[COLOR_NB];
|
||||
CounterMovesHistoryStats CounterMovesHistory;
|
||||
CounterMoveHistoryStats CounterMoveHistory;
|
||||
|
||||
template <NodeType NT>
|
||||
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
|
||||
|
@ -175,12 +175,12 @@ void Search::init() {
|
|||
}
|
||||
|
||||
|
||||
/// Search::clear() resets to zero search state, to obtain reproducible results
|
||||
/// Search::clear() resets search state to zero, to obtain reproducible results
|
||||
|
||||
void Search::clear() {
|
||||
|
||||
TT.clear();
|
||||
CounterMovesHistory.clear();
|
||||
CounterMoveHistory.clear();
|
||||
|
||||
for (Thread* th : Threads)
|
||||
{
|
||||
|
@ -193,7 +193,7 @@ void Search::clear() {
|
|||
|
||||
|
||||
/// 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.
|
||||
/// up to the given depth are generated and counted, and the sum is returned.
|
||||
template<bool Root>
|
||||
uint64_t Search::perft(Position& pos, Depth depth) {
|
||||
|
||||
|
@ -223,8 +223,7 @@ template uint64_t Search::perft<true>(Position&, Depth);
|
|||
|
||||
|
||||
/// MainThread::search() is called by the main thread when the program receives
|
||||
/// the UCI 'go' command. It searches from root position and at the end prints
|
||||
/// the "bestmove" to output.
|
||||
/// the UCI 'go' command. It searches from the root position and outputs the "bestmove".
|
||||
|
||||
void MainThread::search() {
|
||||
|
||||
|
@ -260,8 +259,8 @@ void MainThread::search() {
|
|||
if (TB::Cardinality >= rootPos.count<ALL_PIECES>(WHITE)
|
||||
+ rootPos.count<ALL_PIECES>(BLACK))
|
||||
{
|
||||
// If the current root position is in the tablebases then RootMoves
|
||||
// contains only moves that preserve the draw or win.
|
||||
// If the current root position is in the tablebases, then RootMoves
|
||||
// contains only moves that preserve the draw or the win.
|
||||
TB::RootInTB = Tablebases::root_probe(rootPos, rootMoves, TB::Score);
|
||||
|
||||
if (TB::RootInTB)
|
||||
|
@ -269,7 +268,7 @@ void MainThread::search() {
|
|||
|
||||
else // If DTZ tables are missing, use WDL tables as a fallback
|
||||
{
|
||||
// Filter out moves that do not preserve a draw or win
|
||||
// Filter out moves that do not preserve the draw or the win.
|
||||
TB::RootInTB = Tablebases::root_probe_wdl(rootPos, rootMoves, TB::Score);
|
||||
|
||||
// Only probe during search if winning
|
||||
|
@ -304,7 +303,7 @@ void MainThread::search() {
|
|||
}
|
||||
|
||||
// When playing in 'nodes as time' mode, subtract the searched nodes from
|
||||
// the available ones before to exit.
|
||||
// the available ones before exiting.
|
||||
if (Limits.npmsec)
|
||||
Time.availableNodes += Limits.inc[us] - Threads.nodes_searched();
|
||||
|
||||
|
@ -356,7 +355,7 @@ void MainThread::search() {
|
|||
|
||||
// Thread::search() is the main iterative deepening loop. It calls search()
|
||||
// repeatedly with increasing depth until the allocated thinking time has been
|
||||
// consumed, user stops the search, or the maximum search depth is reached.
|
||||
// consumed, the user stops the search, or the maximum search depth is reached.
|
||||
|
||||
void Thread::search() {
|
||||
|
||||
|
@ -390,11 +389,11 @@ void Thread::search() {
|
|||
|
||||
multiPV = std::min(multiPV, rootMoves.size());
|
||||
|
||||
// Iterative deepening loop until requested to stop or target depth reached
|
||||
// Iterative deepening loop until requested to stop or the target depth is reached.
|
||||
while (++rootDepth < DEPTH_MAX && !Signals.stop && (!Limits.depth || rootDepth <= Limits.depth))
|
||||
{
|
||||
// Set up the new depth for the helper threads skipping in average each
|
||||
// 2nd ply (using a half density map similar to a Hadamard matrix).
|
||||
// Set up the new depths for the helper threads skipping on average every
|
||||
// 2nd ply (using a half-density map similar to a Hadamard matrix).
|
||||
if (!mainThread)
|
||||
{
|
||||
int d = rootDepth + rootPos.game_ply();
|
||||
|
@ -452,14 +451,14 @@ void Thread::search() {
|
|||
// search the already searched PV lines are preserved.
|
||||
std::stable_sort(rootMoves.begin() + PVIdx, rootMoves.end());
|
||||
|
||||
// Write PV back to transposition table in case the relevant
|
||||
// Write PV back to the transposition table in case the relevant
|
||||
// entries have been overwritten during the search.
|
||||
for (size_t i = 0; i <= PVIdx; ++i)
|
||||
rootMoves[i].insert_pv_in_tt(rootPos);
|
||||
|
||||
// If search has been stopped break immediately. Sorting and
|
||||
// If search has been stopped, break immediately. Sorting and
|
||||
// writing PV back to TT is safe because RootMoves is still
|
||||
// valid, although it refers to previous iteration.
|
||||
// valid, although it refers to the previous iteration.
|
||||
if (Signals.stop)
|
||||
break;
|
||||
|
||||
|
@ -536,12 +535,12 @@ void Thread::search() {
|
|||
if (rootDepth > 4 * ONE_PLY && multiPV == 1)
|
||||
Time.pv_instability(mainThread->bestMoveChanges);
|
||||
|
||||
// Stop the search if only one legal move is available or all
|
||||
// of the available time has been used or we matched an easyMove
|
||||
// Stop the search if only one legal move is available, or if all
|
||||
// of the available time has been used, or if we matched an easyMove
|
||||
// from the previous search and just did a fast verification.
|
||||
if ( rootMoves.size() == 1
|
||||
|| Time.elapsed() > Time.available() * ( 640 - 160 * !mainThread->failedLow
|
||||
- 126 * (bestValue >= mainThread->previousMoveScore)
|
||||
|| Time.elapsed() > Time.available() * ( 640 - 160 * !mainThread->failedLow
|
||||
- 126 * (bestValue >= mainThread->previousMoveScore)
|
||||
- 124 * (bestValue >= mainThread->previousMoveScore && !mainThread->failedLow))/640
|
||||
|| ( mainThread->easyMovePlayed = ( rootMoves[0].pv[0] == easyMove
|
||||
&& mainThread->bestMoveChanges < 0.03
|
||||
|
@ -610,7 +609,7 @@ namespace {
|
|||
bestValue = -VALUE_INFINITE;
|
||||
ss->ply = (ss-1)->ply + 1;
|
||||
|
||||
// Check for available remaining time
|
||||
// Check for the available remaining time
|
||||
if (thisThread->resetCalls.load(std::memory_order_relaxed))
|
||||
{
|
||||
thisThread->resetCalls = false;
|
||||
|
@ -853,7 +852,7 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
Square prevSq = to_sq((ss-1)->currentMove);
|
||||
Move cm = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq];
|
||||
const CounterMovesStats& cmh = CounterMovesHistory[pos.piece_on(prevSq)][prevSq];
|
||||
const CounterMoveStats& cmh = CounterMoveHistory[pos.piece_on(prevSq)][prevSq];
|
||||
|
||||
MovePicker mp(pos, ttMove, depth, thisThread->history, cmh, cm, ss);
|
||||
CheckInfo ci(pos);
|
||||
|
@ -1002,13 +1001,14 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
// Decrease reduction for moves with a good history and
|
||||
// increase reduction for moves with a bad history
|
||||
int rDecrease = ( thisThread->history[pos.piece_on(to_sq(move))][to_sq(move)]
|
||||
int rDecrease = ( thisThread->history[pos.piece_on(to_sq(move))][to_sq(move)]
|
||||
+ cmh[pos.piece_on(to_sq(move))][to_sq(move)]) / 14980;
|
||||
r = std::max(DEPTH_ZERO, r - rDecrease * ONE_PLY);
|
||||
|
||||
// Decrease reduction for moves that escape a capture. Filter out castling
|
||||
// moves because are coded as "king captures rook" and break make_move().
|
||||
// Also use see() instead of see_sign() because destination square is empty.
|
||||
// Decrease reduction for moves that escape a capture. Filter out
|
||||
// castling moves, because they are coded as "king captures rook" and
|
||||
// hence break make_move(). Also use see() instead of see_sign(),
|
||||
// because the destination square is empty.
|
||||
if ( r
|
||||
&& type_of(move) == NORMAL
|
||||
&& type_of(pos.piece_on(to_sq(move))) != PAWN
|
||||
|
@ -1024,7 +1024,7 @@ moves_loop: // When in check search starts from here
|
|||
else
|
||||
doFullDepthSearch = !PvNode || moveCount > 1;
|
||||
|
||||
// Step 16. Full depth search, when LMR is skipped or fails high
|
||||
// Step 16. Full depth search when LMR is skipped or fails high
|
||||
if (doFullDepthSearch)
|
||||
value = newDepth < ONE_PLY ?
|
||||
givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
|
||||
|
@ -1033,7 +1033,7 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
// For PV nodes only, do a full PV search on the first move or after a fail
|
||||
// high (in the latter case search only if value < beta), otherwise let the
|
||||
// parent node fail low with value <= alpha and to try another move.
|
||||
// parent node fail low with value <= alpha and try another move.
|
||||
if (PvNode && (moveCount == 1 || (value > alpha && (RootNode || value < beta))))
|
||||
{
|
||||
(ss+1)->pv = pv;
|
||||
|
@ -1050,7 +1050,7 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
|
||||
|
||||
// Step 18. Check for new best move
|
||||
// Step 18. Check for a new best move
|
||||
// Finished searching the move. If a stop occurred, the return value of
|
||||
// the search cannot be trusted, and we return immediately without
|
||||
// updating best move, PV and TT.
|
||||
|
@ -1118,7 +1118,7 @@ moves_loop: // When in check search starts from here
|
|||
quietsSearched[quietCount++] = move;
|
||||
}
|
||||
|
||||
// Following condition would detect a stop only after move loop has been
|
||||
// The following condition would detect a stop only after move loop has been
|
||||
// completed. But in this case bestValue is valid because we have fully
|
||||
// searched our subtree, and we can anyhow save the result in TT.
|
||||
/*
|
||||
|
@ -1128,7 +1128,7 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
// Step 20. Check for mate and stalemate
|
||||
// All legal moves have been searched and if there are no legal moves, it
|
||||
// must be mate or stalemate. If we are in a singular extension search then
|
||||
// must be a mate or a stalemate. If we are in a singular extension search then
|
||||
// return a fail low score.
|
||||
if (!moveCount)
|
||||
bestValue = excludedMove ? alpha
|
||||
|
@ -1148,7 +1148,7 @@ moves_loop: // When in check search starts from here
|
|||
{
|
||||
Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + depth / ONE_PLY - 1);
|
||||
Square prevPrevSq = to_sq((ss - 2)->currentMove);
|
||||
CounterMovesStats& prevCmh = CounterMovesHistory[pos.piece_on(prevPrevSq)][prevPrevSq];
|
||||
CounterMoveStats& prevCmh = CounterMoveHistory[pos.piece_on(prevPrevSq)][prevPrevSq];
|
||||
prevCmh.update(pos.piece_on(prevSq), prevSq, bonus);
|
||||
}
|
||||
|
||||
|
@ -1334,7 +1334,7 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
|
||||
|
||||
// Check for new best move
|
||||
// Check for a new best move
|
||||
if (value > bestValue)
|
||||
{
|
||||
bestValue = value;
|
||||
|
@ -1425,7 +1425,7 @@ moves_loop: // When in check search starts from here
|
|||
Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + depth / ONE_PLY - 1);
|
||||
|
||||
Square prevSq = to_sq((ss-1)->currentMove);
|
||||
CounterMovesStats& cmh = CounterMovesHistory[pos.piece_on(prevSq)][prevSq];
|
||||
CounterMoveStats& cmh = CounterMoveHistory[pos.piece_on(prevSq)][prevSq];
|
||||
Thread* thisThread = pos.this_thread();
|
||||
|
||||
thisThread->history.update(pos.moved_piece(move), to_sq(move), bonus);
|
||||
|
@ -1451,7 +1451,7 @@ moves_loop: // When in check search starts from here
|
|||
&& is_ok((ss-2)->currentMove))
|
||||
{
|
||||
Square prevPrevSq = to_sq((ss-2)->currentMove);
|
||||
CounterMovesStats& prevCmh = CounterMovesHistory[pos.piece_on(prevPrevSq)][prevPrevSq];
|
||||
CounterMoveStats& prevCmh = CounterMoveHistory[pos.piece_on(prevPrevSq)][prevPrevSq];
|
||||
prevCmh.update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY);
|
||||
}
|
||||
}
|
||||
|
@ -1472,8 +1472,8 @@ moves_loop: // When in check search starts from here
|
|||
int maxScore = -VALUE_INFINITE;
|
||||
|
||||
// Choose best move. For each move score we add two terms, both dependent on
|
||||
// weakness. One deterministic and bigger for weaker levels, and one random,
|
||||
// then we choose the move with the resulting highest score.
|
||||
// weakness. One is deterministic and bigger for weaker levels, and one is
|
||||
// random. Then we choose the move with the resulting highest score.
|
||||
for (size_t i = 0; i < multiPV; ++i)
|
||||
{
|
||||
// This is our magic formula
|
||||
|
@ -1603,7 +1603,7 @@ void RootMove::insert_pv_in_tt(Position& pos) {
|
|||
|
||||
|
||||
/// RootMove::extract_ponder_from_tt() is called in case we have no ponder move
|
||||
/// before exiting the search, for instance in case we stop the search during a
|
||||
/// before exiting the search, for instance, in case we stop the search during a
|
||||
/// fail high at root. We try hard to have a ponder move to return to the GUI,
|
||||
/// otherwise in case of 'ponder on' we have nothing to think on.
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
This file may be redistributed and/or modified without restrictions.
|
||||
|
||||
tbcore.c contains engine-independent routines of the tablebase probing code.
|
||||
This file should not need to much adaptation to add tablebase probing to
|
||||
This file should not need too much adaptation to add tablebase probing to
|
||||
a particular engine, provided the engine is written in C or C++.
|
||||
*/
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ using namespace Search;
|
|||
|
||||
ThreadPool Threads; // Global object
|
||||
|
||||
/// Thread constructor launch the thread and then wait until it goes to sleep
|
||||
/// Thread constructor launches the thread and then waits until it goes to sleep
|
||||
/// in idle_loop().
|
||||
|
||||
Thread::Thread() {
|
||||
|
@ -48,7 +48,7 @@ Thread::Thread() {
|
|||
}
|
||||
|
||||
|
||||
/// Thread destructor wait for thread termination before returning
|
||||
/// Thread destructor waits for thread termination before returning
|
||||
|
||||
Thread::~Thread() {
|
||||
|
||||
|
@ -60,7 +60,8 @@ Thread::~Thread() {
|
|||
}
|
||||
|
||||
|
||||
/// Thread::wait_for_search_finished() wait on sleep condition until not searching
|
||||
/// Thread::wait_for_search_finished() waits on sleep condition
|
||||
/// until not searching
|
||||
|
||||
void Thread::wait_for_search_finished() {
|
||||
|
||||
|
@ -69,7 +70,7 @@ void Thread::wait_for_search_finished() {
|
|||
}
|
||||
|
||||
|
||||
/// Thread::wait() wait on sleep condition until condition is true
|
||||
/// Thread::wait() waits on sleep condition until condition is true
|
||||
|
||||
void Thread::wait(std::atomic_bool& condition) {
|
||||
|
||||
|
@ -78,7 +79,7 @@ void Thread::wait(std::atomic_bool& condition) {
|
|||
}
|
||||
|
||||
|
||||
/// Thread::start_searching() wake up the thread that will start the search
|
||||
/// Thread::start_searching() wakes up the thread that will start the search
|
||||
|
||||
void Thread::start_searching(bool resume) {
|
||||
|
||||
|
@ -115,7 +116,7 @@ void Thread::idle_loop() {
|
|||
}
|
||||
|
||||
|
||||
/// ThreadPool::init() create and launch requested threads, that will go
|
||||
/// ThreadPool::init() creates and launches requested threads that will go
|
||||
/// immediately to sleep. We cannot use a constructor because Threads is a
|
||||
/// static object and we need a fully initialized engine at this point due to
|
||||
/// allocation of Endgames in the Thread constructor.
|
||||
|
@ -127,9 +128,9 @@ void ThreadPool::init() {
|
|||
}
|
||||
|
||||
|
||||
/// ThreadPool::exit() terminate threads before the program exits. Cannot be
|
||||
/// ThreadPool::exit() terminates threads before the program exits. Cannot be
|
||||
/// done in destructor because threads must be terminated before deleting any
|
||||
/// static objects, so while still in main().
|
||||
/// static objects while still in main().
|
||||
|
||||
void ThreadPool::exit() {
|
||||
|
||||
|
@ -156,7 +157,7 @@ void ThreadPool::read_uci_options() {
|
|||
}
|
||||
|
||||
|
||||
/// ThreadPool::nodes_searched() return the number of nodes searched
|
||||
/// ThreadPool::nodes_searched() returns the number of nodes searched
|
||||
|
||||
int64_t ThreadPool::nodes_searched() {
|
||||
|
||||
|
@ -167,8 +168,8 @@ int64_t ThreadPool::nodes_searched() {
|
|||
}
|
||||
|
||||
|
||||
/// ThreadPool::start_thinking() wake up the main thread sleeping in idle_loop()
|
||||
/// and start a new search, then return immediately.
|
||||
/// ThreadPool::start_thinking() wakes up the main thread sleeping in idle_loop()
|
||||
/// and starts a new search, then returns immediately.
|
||||
|
||||
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
|
||||
StateStackPtr& states) {
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
#include "thread_win32.h"
|
||||
|
||||
|
||||
/// Thread struct keeps together all the thread related stuff. We also use
|
||||
/// Thread struct keeps together all the thread-related stuff. We also use
|
||||
/// per-thread pawn and material hash tables so that once we get a pointer to an
|
||||
/// entry its life time is unlimited and we don't have to care about someone
|
||||
/// changing the entry under our feet.
|
||||
|
@ -67,7 +67,7 @@ public:
|
|||
Search::RootMoveVector rootMoves;
|
||||
Depth rootDepth;
|
||||
HistoryStats history;
|
||||
MovesStats counterMoves;
|
||||
MoveStats counterMoves;
|
||||
Depth completedDepth;
|
||||
std::atomic_bool resetCalls;
|
||||
};
|
||||
|
@ -84,7 +84,7 @@ struct MainThread : public Thread {
|
|||
};
|
||||
|
||||
|
||||
/// ThreadPool struct handles all the threads related stuff like init, starting,
|
||||
/// ThreadPool struct handles all the threads-related stuff like init, starting,
|
||||
/// parking and, most importantly, launching a thread. All the access to threads
|
||||
/// data is done through this class.
|
||||
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
/// relies on libwinpthread. Currently libwinpthread implements mutexes directly
|
||||
/// on top of Windows semaphores. Semaphores, being kernel objects, require kernel
|
||||
/// mode transition in order to lock or unlock, which is very slow compared to
|
||||
/// interlocked operations (about 30% slower on bench test). To workaround this
|
||||
/// interlocked operations (about 30% slower on bench test). To work around this
|
||||
/// issue, we define our wrappers to the low level Win32 calls. We use critical
|
||||
/// sections to support Windows XP and older versions. Unfortunately, cond_wait()
|
||||
/// is racy between unlock() and WaitForSingleObject() but they have the same
|
||||
/// speed performance of SRW locks.
|
||||
/// speed performance as the SRW locks.
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace {
|
|||
// move_importance() is a skew-logistic function based on naive statistical
|
||||
// analysis of "how many games are still undecided after n half-moves". Game
|
||||
// is considered "undecided" as long as neither side has >275cp advantage.
|
||||
// Data was extracted from CCRL game database with some simple filtering criteria.
|
||||
// Data was extracted from the CCRL game database with some simple filtering criteria.
|
||||
|
||||
double move_importance(int ply) {
|
||||
|
||||
|
@ -66,7 +66,7 @@ namespace {
|
|||
double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance);
|
||||
double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance);
|
||||
|
||||
return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks an explicit cast
|
||||
return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for an explicit cast
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -91,7 +91,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
|
|||
// If we have to play in 'nodes as time' mode, then convert from time
|
||||
// to nodes, and use resulting values in time management formulas.
|
||||
// WARNING: Given npms (nodes per millisecond) must be much lower then
|
||||
// real engine speed to avoid time losses.
|
||||
// the real engine speed to avoid time losses.
|
||||
if (npmsec)
|
||||
{
|
||||
if (!availableNodes) // Only once at game start
|
||||
|
|
Loading…
Add table
Reference in a new issue