#include <cassert>
#include <cmath>
#include <cstring>
-#include <iomanip>
#include <iostream>
#include <sstream>
#include "history.h"
#include "movegen.h"
#include "movepick.h"
+#include "notation.h"
#include "search.h"
#include "timeman.h"
#include "thread.h"
using Eval::evaluate;
using namespace Search;
-// For some reason argument-dependent lookup (ADL) doesn't work for Android's
-// STLPort, so explicitly qualify following functions.
-using std::count;
-using std::find;
-
namespace {
// Set to true to force running with one thread. Used for debugging
bool connected_threat(const Position& pos, Move m, Move threat);
Value refine_eval(const TTEntry* tte, Value ttValue, Value defaultEval);
Move do_skill_level();
- string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE);
- string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]);
string uci_pv(const Position& pos, int depth, Value alpha, Value beta);
- // MovePickerExt class template extends MovePicker and allows to choose at
- // compile time the proper moves source according to the type of node. In the
- // default case we simply create and use a standard MovePicker object.
- template<bool SpNode> struct MovePickerExt : public MovePicker {
-
- MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, Stack* ss, Value b)
- : MovePicker(p, ttm, d, h, ss, b) {}
- };
-
- // In case of a SpNode we use split point's shared MovePicker object as moves source
- template<> struct MovePickerExt<true> : public MovePicker {
-
- MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, Stack* ss, Value b)
- : MovePicker(p, ttm, d, h, ss, b), mp(ss->sp->mp) {}
-
- Move next_move() { return mp->next_move(); }
- MovePicker* mp;
- };
-
// is_dangerous() checks whether a move belongs to some classes of known
// 'dangerous' moves so that we avoid to prune it.
FORCE_INLINE bool is_dangerous(const Position& pos, Move m, bool captureOrPromotion) {
&& type_of(pos.piece_on(to_sq(m))) != PAWN
&& type_of(m) == NORMAL
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
- - PieceValueMidgame[pos.piece_on(to_sq(m))] == VALUE_ZERO))
+ - PieceValue[Mg][pos.piece_on(to_sq(m))] == VALUE_ZERO))
return true;
return false;
/// 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.
-int64_t Search::perft(Position& pos, Depth depth) {
-
- StateInfo st;
- int64_t cnt = 0;
-
- MoveList<LEGAL> ml(pos);
+size_t Search::perft(Position& pos, Depth depth) {
- // At the last ply just return the number of moves (leaf nodes)
+ // At the last ply just return the number of legal moves (leaf nodes)
if (depth == ONE_PLY)
- return ml.size();
+ return MoveList<LEGAL>(pos).size();
+ StateInfo st;
+ size_t cnt = 0;
CheckInfo ci(pos);
- for ( ; !ml.end(); ++ml)
+
+ for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
{
pos.do_move(ml.move(), st, ci, pos.move_gives_check(ml.move(), ci));
cnt += perft(pos, depth - ONE_PLY);
pos.undo_move(ml.move());
}
+
return cnt;
}
{
Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]);
- if (bookMove && count(RootMoves.begin(), RootMoves.end(), bookMove))
+ if (bookMove && std::count(RootMoves.begin(), RootMoves.end(), bookMove))
{
- std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bookMove));
+ std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), bookMove));
goto finalize;
}
}
if (skillBest == MOVE_NONE) // Still unassigned ?
skillBest = do_skill_level();
- std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), skillBest));
+ std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), skillBest));
}
}
Depth R = 3 * ONE_PLY + depth / 4;
// Null move dynamic reduction based on value
- if (refinedValue - PawnValueMidgame > beta)
+ if (refinedValue - PawnValueMg > beta)
R += ONE_PLY;
pos.do_null_move<true>(st);
MovePicker mp(pos, ttMove, H, pos.captured_piece_type());
CheckInfo ci(pos);
- while ((move = mp.next_move()) != MOVE_NONE)
+ while ((move = mp.next_move<false>()) != MOVE_NONE)
if (pos.pl_move_is_legal(move, ci.pinned))
{
ss->currentMove = move;
split_point_start: // At split points actual search starts from here
- MovePickerExt<SpNode> mp(pos, ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta);
+ MovePicker mp(pos, ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta);
CheckInfo ci(pos);
futilityBase = ss->eval + ss->evalMargin;
singularExtensionNode = !RootNode
// Step 11. Loop through moves
// Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs
while ( bestValue < beta
- && (move = mp.next_move()) != MOVE_NONE
+ && (move = mp.next_move<SpNode>()) != MOVE_NONE
&& !thisThread->cutoff_occurred()
&& !Signals.stop)
{
// At root obey the "searchmoves" option and skip moves not listed in Root
// Move List, as a consequence any illegal move is also skipped. In MultiPV
// mode we also skip PV moves which have been already searched.
- if (RootNode && !count(RootMoves.begin() + PVIdx, RootMoves.end(), move))
+ if (RootNode && !std::count(RootMoves.begin() + PVIdx, RootMoves.end(), move))
continue;
// At PV and SpNode nodes we want all moves to be legal since the beginning
if (SpNode)
{
moveCount = ++sp->moveCount;
- lock_release(sp->lock);
+ sp->mutex.unlock();
}
else
moveCount++;
&& (!threatMove || !connected_threat(pos, move, threatMove)))
{
if (SpNode)
- lock_grab(sp->lock);
+ sp->mutex.lock();
continue;
}
if (futilityValue < beta)
{
if (SpNode)
- lock_grab(sp->lock);
+ sp->mutex.lock();
continue;
}
&& pos.see_sign(move) < 0)
{
if (SpNode)
- lock_grab(sp->lock);
+ sp->mutex.lock();
continue;
}
// Step 18. Check for new best move
if (SpNode)
{
- lock_grab(sp->lock);
+ sp->mutex.lock();
bestValue = sp->bestValue;
alpha = sp->alpha;
}
// be trusted, and we don't update the best move and/or PV.
if (RootNode && !Signals.stop)
{
- RootMove& rm = *find(RootMoves.begin(), RootMoves.end(), move);
+ RootMove& rm = *std::find(RootMoves.begin(), RootMoves.end(), move);
// PV move or new best move ?
if (isPvMove || value > alpha)
alpha = bestValue;
futilityBase = ss->eval + evalMargin + FutilityMarginQS;
- enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame;
+ enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMg;
}
// Initialize a MovePicker object for the current position, and prepare
// Loop through the moves until no moves remain or a beta cutoff occurs
while ( bestValue < beta
- && (move = mp.next_move()) != MOVE_NONE)
+ && (move = mp.next_move<false>()) != MOVE_NONE)
{
assert(is_ok(move));
&& !pos.is_passed_pawn_push(move))
{
futilityValue = futilityBase
- + PieceValueEndgame[pos.piece_on(to_sq(move))]
- + (type_of(move) == ENPASSANT ? PawnValueEndgame : VALUE_ZERO);
+ + PieceValue[Eg][pos.piece_on(to_sq(move))]
+ + (type_of(move) == ENPASSANT ? PawnValueEg : VALUE_ZERO);
if (futilityValue < beta)
{
&& givesCheck
&& move != ttMove
&& !pos.is_capture_or_promotion(move)
- && ss->eval + PawnValueMidgame / 4 < beta
+ && ss->eval + PawnValueMg / 4 < beta
&& !check_is_dangerous(pos, move, futilityBase, beta))
continue;
while (b)
{
// Note that here we generate illegal "double move"!
- if (futilityBase + PieceValueEndgame[pos.piece_on(pop_lsb(&b))] >= beta)
+ if (futilityBase + PieceValue[Eg][pos.piece_on(pop_lsb(&b))] >= beta)
return true;
}
// Case 2: If the threatened piece has value less than or equal to the
// value of the threatening piece, don't prune moves which defend it.
if ( pos.is_capture(threat)
- && ( PieceValueMidgame[pos.piece_on(tfrom)] >= PieceValueMidgame[pos.piece_on(tto)]
+ && ( PieceValue[Mg][pos.piece_on(tfrom)] >= PieceValue[Mg][pos.piece_on(tto)]
|| type_of(pos.piece_on(tfrom)) == KING)
&& pos.move_attacks_square(m, tto))
return true;
}
- // score_to_uci() converts a value to a string suitable for use with the UCI
- // protocol specifications:
- //
- // cp <x> The score from the engine's point of view in centipawns.
- // mate <y> Mate in y moves, not plies. If the engine is getting mated
- // use negative values for y.
+ // When playing with strength handicap choose best move among the MultiPV set
+ // using a statistical rule dependent on SkillLevel. Idea by Heinz van Saanen.
- string score_to_uci(Value v, Value alpha, Value beta) {
+ Move do_skill_level() {
- std::stringstream s;
+ assert(MultiPV > 1);
- if (abs(v) < VALUE_MATE_IN_MAX_PLY)
- s << "cp " << v * 100 / int(PawnValueMidgame);
- else
- s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
+ static RKISS rk;
- s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
+ // PRNG sequence should be not deterministic
+ for (int i = Time::current_time().msec() % 50; i > 0; i--)
+ rk.rand<unsigned>();
- return s.str();
+ // RootMoves are already sorted by score in descending order
+ size_t size = std::min(MultiPV, RootMoves.size());
+ int variance = std::min(RootMoves[0].score - RootMoves[size - 1].score, PawnValueMg);
+ int weakness = 120 - 2 * SkillLevel;
+ int max_s = -VALUE_INFINITE;
+ Move best = MOVE_NONE;
+
+ // Choose best move. For each move score we add two terms both dependent on
+ // weakness, one deterministic and bigger for weaker moves, and one random,
+ // then we choose the move with the resulting highest score.
+ for (size_t i = 0; i < size; i++)
+ {
+ int s = RootMoves[i].score;
+
+ // Don't allow crazy blunders even at very low skills
+ if (i > 0 && RootMoves[i-1].score > s + EasyMoveMargin)
+ break;
+
+ // This is our magic formula
+ s += ( weakness * int(RootMoves[0].score - s)
+ + variance * (rk.rand<unsigned>() % weakness)) / 128;
+
+ if (s > max_s)
+ {
+ max_s = s;
+ best = RootMoves[i].pv[0];
+ }
+ }
+ return best;
}
int t = SearchTime.elapsed();
int selDepth = 0;
- for (int i = 0; i < Threads.size(); i++)
+ for (size_t i = 0; i < Threads.size(); i++)
if (Threads[i].maxPly > selDepth)
selDepth = Threads[i].maxPly;
return s.str();
}
-
- // pretty_pv() formats human-readable search information, typically to be
- // appended to the search log file. It uses the two helpers below to pretty
- // format time and score respectively.
-
- string time_to_string(int millisecs) {
-
- const int MSecMinute = 1000 * 60;
- const int MSecHour = 1000 * 60 * 60;
-
- int hours = millisecs / MSecHour;
- int minutes = (millisecs % MSecHour) / MSecMinute;
- int seconds = ((millisecs % MSecHour) % MSecMinute) / 1000;
-
- std::stringstream s;
-
- if (hours)
- s << hours << ':';
-
- s << std::setfill('0') << std::setw(2) << minutes << ':'
- << std::setw(2) << seconds;
- return s.str();
- }
-
- string score_to_string(Value v) {
-
- std::stringstream s;
-
- if (v >= VALUE_MATE_IN_MAX_PLY)
- s << "#" << (VALUE_MATE - v + 1) / 2;
-
- else if (v <= VALUE_MATED_IN_MAX_PLY)
- s << "-#" << (VALUE_MATE + v) / 2;
-
- else
- s << std::setprecision(2) << std::fixed << std::showpos
- << float(v) / PawnValueMidgame;
-
- return s.str();
- }
-
- string pretty_pv(Position& pos, int depth, Value value, int time, Move pv[]) {
-
- const int64_t K = 1000;
- const int64_t M = 1000000;
-
- StateInfo state[MAX_PLY_PLUS_2], *st = state;
- Move* m = pv;
- string san, padding;
- size_t length;
- std::stringstream s;
-
- s << std::setw(2) << depth
- << std::setw(8) << score_to_string(value)
- << std::setw(8) << time_to_string(time);
-
- if (pos.nodes_searched() < M)
- s << std::setw(8) << pos.nodes_searched() / 1 << " ";
-
- else if (pos.nodes_searched() < K * M)
- s << std::setw(7) << pos.nodes_searched() / K << "K ";
-
- else
- s << std::setw(7) << pos.nodes_searched() / M << "M ";
-
- padding = string(s.str().length(), ' ');
- length = padding.length();
-
- while (*m != MOVE_NONE)
- {
- san = move_to_san(pos, *m);
-
- if (length + san.length() > 80)
- {
- s << "\n" + padding;
- length = padding.length();
- }
-
- s << san << ' ';
- length += san.length() + 1;
-
- pos.do_move(*m++, *st++);
- }
-
- while (m != pv)
- pos.undo_move(*--m);
-
- return s.str();
- }
-
-
- // When playing with strength handicap choose best move among the MultiPV set
- // using a statistical rule dependent on SkillLevel. Idea by Heinz van Saanen.
-
- Move do_skill_level() {
-
- assert(MultiPV > 1);
-
- static RKISS rk;
-
- // PRNG sequence should be not deterministic
- for (int i = Time::current_time().msec() % 50; i > 0; i--)
- rk.rand<unsigned>();
-
- // RootMoves are already sorted by score in descending order
- size_t size = std::min(MultiPV, RootMoves.size());
- int variance = std::min(RootMoves[0].score - RootMoves[size - 1].score, PawnValueMidgame);
- int weakness = 120 - 2 * SkillLevel;
- int max_s = -VALUE_INFINITE;
- Move best = MOVE_NONE;
-
- // Choose best move. For each move score we add two terms both dependent on
- // weakness, one deterministic and bigger for weaker moves, and one random,
- // then we choose the move with the resulting highest score.
- for (size_t i = 0; i < size; i++)
- {
- int s = RootMoves[i].score;
-
- // Don't allow crazy blunders even at very low skills
- if (i > 0 && RootMoves[i-1].score > s + EasyMoveMargin)
- break;
-
- // This is our magic formula
- s += ( weakness * int(RootMoves[0].score - s)
- + variance * (rk.rand<unsigned>() % weakness)) / 128;
-
- if (s > max_s)
- {
- max_s = s;
- best = RootMoves[i].pv[0];
- }
- }
- return best;
- }
-
} // namespace
}
-/// Thread::idle_loop() is where the thread is parked when it has no work to do.
-/// The parameter 'master_sp', if non-NULL, is a pointer to an active SplitPoint
-/// object for which the thread is the master.
+/// Thread::idle_loop() is where the thread is parked when it has no work to do
-void Thread::idle_loop(SplitPoint* sp_master) {
+void Thread::idle_loop() {
+
+ // Pointer 'sp_master', if non-NULL, points to the active SplitPoint
+ // object for which the thread is the master.
+ const SplitPoint* sp_master = splitPointsCnt ? curSplitPoint : NULL;
+
+ assert(!sp_master || (sp_master->master == this && is_searching));
// If this thread is the master of a split point and all slaves have
// finished their work at this split point, return from the idle loop.
}
// Grab the lock to avoid races with Thread::wake_up()
- lock_grab(sleepLock);
+ mutex.lock();
// If we are master and all slaves have finished don't go to sleep
if (sp_master && !sp_master->slavesMask)
{
- lock_release(sleepLock);
+ mutex.unlock();
break;
}
// in the meanwhile, allocated us and sent the wake_up() call before we
// had the chance to grab the lock.
if (do_sleep || !is_searching)
- cond_wait(sleepCond, sleepLock);
+ sleepCondition.wait(mutex);
- lock_release(sleepLock);
+ mutex.unlock();
}
// If this thread has been assigned work, launch a search
{
assert(!do_sleep && !do_exit);
- lock_grab(Threads.splitLock);
+ Threads.mutex.lock();
assert(is_searching);
SplitPoint* sp = curSplitPoint;
- lock_release(Threads.splitLock);
+ Threads.mutex.unlock();
Stack ss[MAX_PLY_PLUS_2];
Position pos(*sp->pos, this);
memcpy(ss, sp->ss - 1, 4 * sizeof(Stack));
(ss+1)->sp = sp;
- lock_grab(sp->lock);
+ sp->mutex.lock();
if (sp->nodeType == Root)
search<SplitPointRoot>(pos, ss+1, sp->alpha, sp->beta, sp->depth);
// case we are the last slave of the split point.
if ( Threads.use_sleeping_threads()
&& this != sp->master
- && !sp->master->is_searching)
+ && !sp->slavesMask)
+ {
+ assert(!sp->master->is_searching);
sp->master->wake_up();
+ }
// After releasing the lock we cannot access anymore any SplitPoint
// related data in a safe way becuase it could have been released under
// our feet by the sp master. Also accessing other Thread objects is
// unsafe because if we are exiting there is a chance are already freed.
- lock_release(sp->lock);
+ sp->mutex.unlock();
}
}
}