int ActiveThreads;
volatile bool AllThreadsShouldExit, AllThreadsShouldSleep;
Thread threads[MAX_THREADS];
- SplitPoint SplitPointStack[MAX_THREADS][ACTIVE_SPLIT_POINTS_MAX];
Lock MPLock, WaitLock;
// better than the second best move.
const Value EasyMoveMargin = Value(0x200);
- // Last seconds noise filtering (LSN)
- const bool UseLSNFiltering = true;
- const int LSNTime = 100; // In milliseconds
- const Value LSNValue = value_from_centipawns(200);
- bool loseOnTime = false;
-
/// Global variables
bool connected_moves(const Position& pos, Move m1, Move m2);
bool value_is_mate(Value value);
+ Value value_to_tt(Value v, int ply);
+ Value value_from_tt(Value v, int ply);
bool move_is_killer(Move m, SearchStack* ss);
bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply);
bool connected_threat(const Position& pos, Move m, Move threat);
void update_gains(const Position& pos, Move move, Value before, Value after);
int current_search_time();
+ std::string value_to_uci(Value v);
int nps();
void poll();
void ponderhit();
// Init reductions array
for (hd = 1; hd < 64; hd++) for (mc = 1; mc < 64; mc++)
{
- double pvRed = log(double(hd)) * log(double(mc)) / 3.0;
- double nonPVRed = log(double(hd)) * log(double(mc)) / 1.5;
+ double pvRed = 0.33 + log(double(hd)) * log(double(mc)) / 4.5;
+ double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25;
ReductionMatrix[PV][hd][mc] = (int8_t) ( pvRed >= 1.0 ? floor( pvRed * int(OnePly)) : 0);
ReductionMatrix[NonPV][hd][mc] = (int8_t) (nonPVRed >= 1.0 ? floor(nonPVRed * int(OnePly)) : 0);
}
void SearchStack::init() {
currentMove = threatMove = bestMove = MOVE_NONE;
- reduction = Depth(0);
- eval = VALUE_NONE;
}
// SearchStack::initKillers() initializes killers for a search stack entry
/// search-related global variables, and calls root_search(). It returns false
/// when a quit command is received during the search.
-bool think(const Position& pos, bool infinite, bool ponder, int side_to_move,
- int time[], int increment[], int movesToGo, int maxDepth,
- int maxNodes, int maxTime, Move searchMoves[]) {
+bool think(const Position& pos, bool infinite, bool ponder, int time[], int increment[],
+ int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]) {
// Initialize global search variables
StopOnPonderhit = AbortSearch = Quit = AspirationFailLow = false;
}
}
- // Reset loseOnTime flag at the beginning of a new game
- if (button_was_pressed("New Game"))
- loseOnTime = false;
-
// Read UCI option values
TT.set_size(get_option_value_int("Hash"));
if (button_was_pressed("Clear Hash"))
TM.wake_sleeping_threads();
// Set thinking time
- int myTime = time[side_to_move];
- int myIncrement = increment[side_to_move];
+ int myTime = time[pos.side_to_move()];
+ int myIncrement = increment[pos.side_to_move()];
if (UseTimeManagement)
{
if (!movesToGo) // Sudden death time control
<< " increment: " << myIncrement
<< " moves to go: " << movesToGo << endl;
- // LSN filtering. Used only for developing purposes, disabled by default
- if ( UseLSNFiltering
- && loseOnTime)
- {
- // Step 2. If after last move we decided to lose on time, do it now!
- while (SearchStartTime + myTime + 1000 > get_system_time())
- /* wait here */;
- }
-
// We're ready to start thinking. Call the iterative deepening loop function
- Value v = id_loop(pos, searchMoves);
-
- if (UseLSNFiltering)
- {
- // Step 1. If this is sudden death game and our position is hopeless,
- // decide to lose on time.
- if ( !loseOnTime // If we already lost on time, go to step 3.
- && myTime < LSNTime
- && myIncrement == 0
- && movesToGo == 0
- && v < -LSNValue)
- {
- loseOnTime = true;
- }
- else if (loseOnTime)
- {
- // Step 3. Now after stepping over the time limit, reset flag for next match.
- loseOnTime = false;
- }
- }
+ id_loop(pos, searchMoves);
if (UseLogFile)
LogFile.close();
// so to output information also for iteration 1.
cout << "info depth " << 1
<< "\ninfo depth " << 1
- << " score " << value_to_string(rml.get_move_score(0))
+ << " score " << value_to_uci(rml.get_move_score(0))
<< " time " << current_search_time()
<< " nodes " << TM.nodes_searched()
<< " nps " << nps()
// Print final search statistics
cout << "info nodes " << TM.nodes_searched()
<< " nps " << nps()
- << " time " << current_search_time()
- << " hashfull " << TT.full() << endl;
+ << " time " << current_search_time() << endl;
// Print the best move and the ponder move to the standard output
if (pv[0] == MOVE_NONE)
// Step 5. Evaluate the position statically
// At root we do this only to get reference value for child nodes
- if (!isCheck)
- ss->eval = evaluate(pos, ei);
+ ss->eval = isCheck ? VALUE_NONE : evaluate(pos, ei);
// Step 6. Razoring (omitted at root)
// Step 7. Static null move pruning (omitted at root)
for (int j = 0; j < Min(MultiPV, rml.move_count()); j++)
{
cout << "info multipv " << j + 1
- << " score " << value_to_string(rml.get_move_score(j))
+ << " score " << value_to_uci(rml.get_move_score(j))
<< " depth " << (j <= i ? Iteration : Iteration - 1)
<< " time " << current_search_time()
<< " nodes " << TM.nodes_searched()
refinedValue = refine_eval(tte, ss->eval, ply); // Enhance accuracy with TT value if possible
update_gains(pos, (ss-1)->currentMove, (ss-1)->eval, ss->eval);
}
+ else
+ ss->eval = VALUE_NONE;
// Step 6. Razoring (is omitted in PV nodes)
if ( !PvNode
if (nullValue >= value_mate_in(PLY_MAX))
nullValue = beta;
- // Do zugzwang verification search at high depths
if (depth < 6 * OnePly)
return nullValue;
+ // Do verification search at high depths
ss->skipNullMove = true;
- Value v = search<NonPV>(pos, ss, alpha, beta, depth-5*OnePly, ply);
+ Value v = search<NonPV>(pos, ss, alpha, beta, depth-R*OnePly, ply);
ss->skipNullMove = false;
if (v >= beta)
// Step 11. Decide the new search depth
ext = extension<PvNode>(pos, move, captureOrPromotion, moveIsCheck, singleEvasion, mateThreat, &dangerous);
- // Singular extension search. We extend the TT move if its value is much better than
- // its siblings. To verify this we do a reduced search on all the other moves but the
- // ttMove, if result is lower then ttValue minus a margin then we extend ttMove.
+ // Singular extension search. If all moves but one fail low on a search of (alpha-s, beta-s),
+ // and just one fails high on (alpha, beta), then that move is singular and should be extended.
+ // To verify this we do a reduced search on all the other moves but the ttMove, if result is
+ // lower then ttValue minus a margin then we extend ttMove.
if ( singularExtensionNode
&& move == tte->move()
&& ext < OnePly)
Value v = search<NonPV>(pos, ss, b - 1, b, depth / 2, ply);
ss->skipNullMove = false;
ss->excludedMove = MOVE_NONE;
- if (v < ttValue - SingularExtensionMargin)
+ if (v < b)
ext = OnePly;
}
}
TM.incrementNodeCounter(pos.thread());
ss->bestMove = ss->currentMove = MOVE_NONE;
- ss->eval = VALUE_NONE;
// Check for an instant draw or maximum ply reached
if (pos.is_draw() || ply >= PLY_MAX - 1)
if (isCheck)
{
bestValue = futilityBase = -VALUE_INFINITE;
+ ss->eval = VALUE_NONE;
deepChecks = enoughMaterial = false;
}
else
}
- // value_is_mate() checks if the given value is a mate one
- // eventually compensated for the ply.
+ // value_is_mate() checks if the given value is a mate one eventually
+ // compensated for the ply.
bool value_is_mate(Value value) {
}
- // move_is_killer() checks if the given move is among the
- // killer moves of that ply.
+ // value_to_tt() adjusts a mate score from "plies to mate from the root" to
+ // "plies to mate from the current ply". Non-mate scores are unchanged.
+ // The function is called before storing a value to the transposition table.
+
+ Value value_to_tt(Value v, int ply) {
+
+ if (v >= value_mate_in(PLY_MAX))
+ return v + ply;
+
+ if (v <= value_mated_in(PLY_MAX))
+ return v - ply;
+
+ return v;
+ }
+
+
+ // value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score from
+ // the transposition table to a mate score corrected for the current ply.
+
+ Value value_from_tt(Value v, int ply) {
+
+ if (v >= value_mate_in(PLY_MAX))
+ return v - ply;
+
+ if (v <= value_mated_in(PLY_MAX))
+ return v + ply;
+
+ return v;
+ }
+
+
+ // move_is_killer() checks if the given move is among the killer moves
bool move_is_killer(Move m, SearchStack* ss) {
}
+ // value_to_uci() converts a value to a string suitable for use with the UCI protocol
+
+ std::string value_to_uci(Value v) {
+
+ std::stringstream s;
+
+ if (abs(v) < VALUE_MATE - PLY_MAX * OnePly)
+ s << "cp " << int(v) * 100 / int(PawnValueMidgame); // Scale to pawn = 100
+ else
+ s << "mate " << (v > 0 ? (VALUE_MATE - v + 1) / 2 : -(VALUE_MATE + v) / 2 );
+
+ return s.str();
+ }
+
// nps() computes the current nodes/second count.
int nps() {
dbg_print_hit_rate();
cout << "info nodes " << TM.nodes_searched() << " nps " << nps()
- << " time " << t << " hashfull " << TT.full() << endl;
+ << " time " << t << endl;
}
// Should we stop the search?
{
ss->excludedMove = MOVE_NONE;
ss->skipNullMove = false;
+ ss->reduction = Depth(0);
if (i < 3)
{
void print_pv_info(const Position& pos, Move pv[], Value alpha, Value beta, Value value) {
cout << "info depth " << Iteration
- << " score " << value_to_string(value)
+ << " score " << value_to_uci(value)
<< (value >= beta ? " lowerbound" : value <= alpha ? " upperbound" : "")
<< " time " << current_search_time()
<< " nodes " << TM.nodes_searched()
SitIdleEvent[i] = CreateEvent(0, FALSE, FALSE, 0);
#endif
- // Initialize SplitPointStack locks
+ // Initialize splitPoints[] locks
for (i = 0; i < MAX_THREADS; i++)
- for (int j = 0; j < ACTIVE_SPLIT_POINTS_MAX; j++)
- lock_init(&(SplitPointStack[i][j].lock), NULL);
+ for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
+ lock_init(&(threads[i].splitPoints[j].lock), NULL);
// Will be set just before program exits to properly end the threads
AllThreadsShouldExit = false;
// Now we can safely destroy the locks
for (int i = 0; i < MAX_THREADS; i++)
- for (int j = 0; j < ACTIVE_SPLIT_POINTS_MAX; j++)
- lock_destroy(&(SplitPointStack[i][j].lock));
+ for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
+ lock_destroy(&(threads[i].splitPoints[j].lock));
lock_destroy(&WaitLock);
lock_destroy(&MPLock);
// Apply the "helpful master" concept if possible. Use localActiveSplitPoints
// that is known to be > 0, instead of threads[slave].activeSplitPoints that
// could have been set to 0 by another thread leading to an out of bound access.
- if (SplitPointStack[slave][localActiveSplitPoints - 1].slaves[master])
+ if (threads[slave].splitPoints[localActiveSplitPoints - 1].slaves[master])
return true;
return false;
assert(p.thread() >= 0 && p.thread() < ActiveThreads);
assert(ActiveThreads > 1);
- int master = p.thread();
+ int i, master = p.thread();
+ Thread& masterThread = threads[master];
lock_grab(&MPLock);
// If no other thread is available to help us, or if we have too many
// active split points, don't split.
if ( !available_thread_exists(master)
- || threads[master].activeSplitPoints >= ACTIVE_SPLIT_POINTS_MAX)
+ || masterThread.activeSplitPoints >= MAX_ACTIVE_SPLIT_POINTS)
{
lock_release(&MPLock);
return;
}
// Pick the next available split point object from the split point stack
- SplitPoint* splitPoint = &SplitPointStack[master][threads[master].activeSplitPoints];
+ SplitPoint& splitPoint = masterThread.splitPoints[masterThread.activeSplitPoints++];
// Initialize the split point object
- splitPoint->parent = threads[master].splitPoint;
- splitPoint->stopRequest = false;
- splitPoint->ply = ply;
- splitPoint->depth = depth;
- splitPoint->mateThreat = mateThreat;
- splitPoint->alpha = *alpha;
- splitPoint->beta = beta;
- splitPoint->pvNode = pvNode;
- splitPoint->bestValue = *bestValue;
- splitPoint->mp = mp;
- splitPoint->moveCount = *moveCount;
- splitPoint->pos = &p;
- splitPoint->parentSstack = ss;
- for (int i = 0; i < ActiveThreads; i++)
- splitPoint->slaves[i] = 0;
-
- threads[master].splitPoint = splitPoint;
- threads[master].activeSplitPoints++;
+ splitPoint.parent = masterThread.splitPoint;
+ splitPoint.stopRequest = false;
+ splitPoint.ply = ply;
+ splitPoint.depth = depth;
+ splitPoint.mateThreat = mateThreat;
+ splitPoint.alpha = *alpha;
+ splitPoint.beta = beta;
+ splitPoint.pvNode = pvNode;
+ splitPoint.bestValue = *bestValue;
+ splitPoint.mp = mp;
+ splitPoint.moveCount = *moveCount;
+ splitPoint.pos = &p;
+ splitPoint.parentSstack = ss;
+ for (i = 0; i < ActiveThreads; i++)
+ splitPoint.slaves[i] = 0;
+
+ masterThread.splitPoint = &splitPoint;
// If we are here it means we are not available
- assert(threads[master].state != THREAD_AVAILABLE);
+ assert(masterThread.state != THREAD_AVAILABLE);
int workersCnt = 1; // At least the master is included
// Allocate available threads setting state to THREAD_BOOKED
- for (int i = 0; !Fake && i < ActiveThreads && workersCnt < MaxThreadsPerSplitPoint; i++)
+ for (i = 0; !Fake && i < ActiveThreads && workersCnt < MaxThreadsPerSplitPoint; i++)
if (thread_is_available(i, master))
{
threads[i].state = THREAD_BOOKED;
- threads[i].splitPoint = splitPoint;
- splitPoint->slaves[i] = 1;
+ threads[i].splitPoint = &splitPoint;
+ splitPoint.slaves[i] = 1;
workersCnt++;
}
// Tell the threads that they have work to do. This will make them leave
// their idle loop. But before copy search stack tail for each thread.
- for (int i = 0; i < ActiveThreads; i++)
- if (i == master || splitPoint->slaves[i])
+ for (i = 0; i < ActiveThreads; i++)
+ if (i == master || splitPoint.slaves[i])
{
- memcpy(splitPoint->sstack[i], ss - 1, 4 * sizeof(SearchStack));
+ memcpy(splitPoint.sstack[i], ss - 1, 4 * sizeof(SearchStack));
assert(i == master || threads[i].state == THREAD_BOOKED);
// THREAD_WORKISWAITING. We send the split point as a second parameter to the
// idle loop, which means that the main thread will return from the idle
// loop when all threads have finished their work at this split point.
- idle_loop(master, splitPoint);
+ idle_loop(master, &splitPoint);
// We have returned from the idle loop, which means that all threads are
// finished. Update alpha and bestValue, and return.
lock_grab(&MPLock);
- *alpha = splitPoint->alpha;
- *bestValue = splitPoint->bestValue;
- threads[master].activeSplitPoints--;
- threads[master].splitPoint = splitPoint->parent;
+ *alpha = splitPoint.alpha;
+ *bestValue = splitPoint.bestValue;
+ masterThread.activeSplitPoints--;
+ masterThread.splitPoint = splitPoint.parent;
lock_release(&MPLock);
}
// Find a quick score for the move
init_ss_array(ss, PLY_MAX_PLUS_2);
+ ss[0].eval = VALUE_NONE;
+ ss[0].currentMove = cur->move;
pos.do_move(cur->move, st);
moves[count].move = cur->move;
moves[count].score = -qsearch<PV>(pos, ss+1, -VALUE_INFINITE, VALUE_INFINITE, Depth(0), 1);