mirror of
https://github.com/sockspls/badfish
synced 2025-04-30 08:43:09 +00:00
Remove more files
This commit is contained in:
parent
748749dd4a
commit
ece8f0780f
12 changed files with 0 additions and 3337 deletions
98
src/psqtab.h
98
src/psqtab.h
|
@ -1,98 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PSQTAB_H_INCLUDED
|
|
||||||
#define PSQTAB_H_INCLUDED
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
#define S(mg, eg) make_score(mg, eg)
|
|
||||||
|
|
||||||
|
|
||||||
/// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on
|
|
||||||
/// a given square a (middlegame, endgame) score pair is assigned. PSQT is defined
|
|
||||||
/// for the white side and the tables are symmetric for the black side.
|
|
||||||
|
|
||||||
static const Score PSQT[][SQUARE_NB] = {
|
|
||||||
{ },
|
|
||||||
{ // Pawn
|
|
||||||
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0),
|
|
||||||
S(-20, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S(-20, 0),
|
|
||||||
S(-20, 0), S( 0, 0), S(10, 0), S(20, 0), S(20, 0), S(10, 0), S( 0, 0), S(-20, 0),
|
|
||||||
S(-20, 0), S( 0, 0), S(20, 0), S(40, 0), S(40, 0), S(20, 0), S( 0, 0), S(-20, 0),
|
|
||||||
S(-20, 0), S( 0, 0), S(10, 0), S(20, 0), S(20, 0), S(10, 0), S( 0, 0), S(-20, 0),
|
|
||||||
S(-20, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S(-20, 0),
|
|
||||||
S(-20, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S(-20, 0),
|
|
||||||
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0)
|
|
||||||
},
|
|
||||||
{ // Knight
|
|
||||||
S(-144,-98), S(-109,-83), S(-85,-51), S(-73,-16), S(-73,-16), S(-85,-51), S(-109,-83), S(-144,-98),
|
|
||||||
S( -88,-68), S( -43,-53), S(-19,-21), S( -7, 14), S( -7, 14), S(-19,-21), S( -43,-53), S( -88,-68),
|
|
||||||
S( -69,-53), S( -24,-38), S( 0, -6), S( 12, 29), S( 12, 29), S( 0, -6), S( -24,-38), S( -69,-53),
|
|
||||||
S( -28,-42), S( 17,-27), S( 41, 5), S( 53, 40), S( 53, 40), S( 41, 5), S( 17,-27), S( -28,-42),
|
|
||||||
S( -30,-42), S( 15,-27), S( 39, 5), S( 51, 40), S( 51, 40), S( 39, 5), S( 15,-27), S( -30,-42),
|
|
||||||
S( -10,-53), S( 35,-38), S( 59, -6), S( 71, 29), S( 71, 29), S( 59, -6), S( 35,-38), S( -10,-53),
|
|
||||||
S( -64,-68), S( -19,-53), S( 5,-21), S( 17, 14), S( 17, 14), S( 5,-21), S( -19,-53), S( -64,-68),
|
|
||||||
S(-200,-98), S( -65,-83), S(-41,-51), S(-29,-16), S(-29,-16), S(-41,-51), S( -65,-83), S(-200,-98)
|
|
||||||
},
|
|
||||||
{ // Bishop
|
|
||||||
S(-54,-65), S(-27,-42), S(-34,-44), S(-43,-26), S(-43,-26), S(-34,-44), S(-27,-42), S(-54,-65),
|
|
||||||
S(-29,-43), S( 8,-20), S( 1,-22), S( -8, -4), S( -8, -4), S( 1,-22), S( 8,-20), S(-29,-43),
|
|
||||||
S(-20,-33), S( 17,-10), S( 10,-12), S( 1, 6), S( 1, 6), S( 10,-12), S( 17,-10), S(-20,-33),
|
|
||||||
S(-19,-35), S( 18,-12), S( 11,-14), S( 2, 4), S( 2, 4), S( 11,-14), S( 18,-12), S(-19,-35),
|
|
||||||
S(-22,-35), S( 15,-12), S( 8,-14), S( -1, 4), S( -1, 4), S( 8,-14), S( 15,-12), S(-22,-35),
|
|
||||||
S(-28,-33), S( 9,-10), S( 2,-12), S( -7, 6), S( -7, 6), S( 2,-12), S( 9,-10), S(-28,-33),
|
|
||||||
S(-32,-43), S( 5,-20), S( -2,-22), S(-11, -4), S(-11, -4), S( -2,-22), S( 5,-20), S(-32,-43),
|
|
||||||
S(-49,-65), S(-22,-42), S(-29,-44), S(-38,-26), S(-38,-26), S(-29,-44), S(-22,-42), S(-49,-65)
|
|
||||||
},
|
|
||||||
{ // Rook
|
|
||||||
S(-22, 3), S(-17, 3), S(-12, 3), S(-8, 3), S(-8, 3), S(-12, 3), S(-17, 3), S(-22, 3),
|
|
||||||
S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
|
|
||||||
S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
|
|
||||||
S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
|
|
||||||
S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
|
|
||||||
S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
|
|
||||||
S(-11, 3), S( 4, 3), S( 9, 3), S(13, 3), S(13, 3), S( 9, 3), S( 4, 3), S(-11, 3),
|
|
||||||
S(-22, 3), S(-17, 3), S(-12, 3), S(-8, 3), S(-8, 3), S(-12, 3), S(-17, 3), S(-22, 3)
|
|
||||||
},
|
|
||||||
{ // Queen
|
|
||||||
S(-2,-80), S(-2,-54), S(-2,-42), S(-2,-30), S(-2,-30), S(-2,-42), S(-2,-54), S(-2,-80),
|
|
||||||
S(-2,-54), S( 8,-30), S( 8,-18), S( 8, -6), S( 8, -6), S( 8,-18), S( 8,-30), S(-2,-54),
|
|
||||||
S(-2,-42), S( 8,-18), S( 8, -6), S( 8, 6), S( 8, 6), S( 8, -6), S( 8,-18), S(-2,-42),
|
|
||||||
S(-2,-30), S( 8, -6), S( 8, 6), S( 8, 18), S( 8, 18), S( 8, 6), S( 8, -6), S(-2,-30),
|
|
||||||
S(-2,-30), S( 8, -6), S( 8, 6), S( 8, 18), S( 8, 18), S( 8, 6), S( 8, -6), S(-2,-30),
|
|
||||||
S(-2,-42), S( 8,-18), S( 8, -6), S( 8, 6), S( 8, 6), S( 8, -6), S( 8,-18), S(-2,-42),
|
|
||||||
S(-2,-54), S( 8,-30), S( 8,-18), S( 8, -6), S( 8, -6), S( 8,-18), S( 8,-30), S(-2,-54),
|
|
||||||
S(-2,-80), S(-2,-54), S(-2,-42), S(-2,-30), S(-2,-30), S(-2,-42), S(-2,-54), S(-2,-80)
|
|
||||||
},
|
|
||||||
{ // King
|
|
||||||
S(298, 27), S(332, 81), S(273,108), S(225,116), S(225,116), S(273,108), S(332, 81), S(298, 27),
|
|
||||||
S(287, 74), S(321,128), S(262,155), S(214,163), S(214,163), S(262,155), S(321,128), S(287, 74),
|
|
||||||
S(224,111), S(258,165), S(199,192), S(151,200), S(151,200), S(199,192), S(258,165), S(224,111),
|
|
||||||
S(196,135), S(230,189), S(171,216), S(123,224), S(123,224), S(171,216), S(230,189), S(196,135),
|
|
||||||
S(173,135), S(207,189), S(148,216), S(100,224), S(100,224), S(148,216), S(207,189), S(173,135),
|
|
||||||
S(146,111), S(180,165), S(121,192), S( 73,200), S( 73,200), S(121,192), S(180,165), S(146,111),
|
|
||||||
S(119, 74), S(153,128), S( 94,155), S( 46,163), S( 46,163), S( 94,155), S(153,128), S(119, 74),
|
|
||||||
S( 98, 27), S(132, 81), S( 73,108), S( 25,116), S( 25,116), S( 73,108), S(132, 81), S( 98, 27)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#undef S
|
|
||||||
|
|
||||||
#endif // #ifndef PSQTAB_H_INCLUDED
|
|
1678
src/search.cpp
1678
src/search.cpp
File diff suppressed because it is too large
Load diff
113
src/search.h
113
src/search.h
|
@ -1,113 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SEARCH_H_INCLUDED
|
|
||||||
#define SEARCH_H_INCLUDED
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <stack>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "misc.h"
|
|
||||||
#include "position.h"
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
struct SplitPoint;
|
|
||||||
|
|
||||||
namespace Search {
|
|
||||||
|
|
||||||
/// The Stack struct keeps track of the information we need to remember from
|
|
||||||
/// nodes shallower and deeper in the tree during the search. Each search thread
|
|
||||||
/// has its own array of Stack objects, indexed by the current ply.
|
|
||||||
|
|
||||||
struct Stack {
|
|
||||||
SplitPoint* splitPoint;
|
|
||||||
Move* pv;
|
|
||||||
int ply;
|
|
||||||
Move currentMove;
|
|
||||||
Move ttMove;
|
|
||||||
Move excludedMove;
|
|
||||||
Move killers[2];
|
|
||||||
Depth reduction;
|
|
||||||
Value staticEval;
|
|
||||||
bool skipEarlyPruning;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// RootMove struct is used for moves at the root of the tree. For each root
|
|
||||||
/// move we store a score, a node count, and a PV (really a refutation in the
|
|
||||||
/// case of moves which fail low). Score is normally set at -VALUE_INFINITE for
|
|
||||||
/// all non-pv moves.
|
|
||||||
struct RootMove {
|
|
||||||
|
|
||||||
RootMove(Move m) : score(-VALUE_INFINITE), previousScore(-VALUE_INFINITE), pv(1, m) {}
|
|
||||||
|
|
||||||
bool operator<(const RootMove& m) const { return score > m.score; } // Ascending sort
|
|
||||||
bool operator==(const Move& m) const { return pv[0] == m; }
|
|
||||||
|
|
||||||
void insert_pv_in_tt(Position& pos);
|
|
||||||
|
|
||||||
Value score;
|
|
||||||
Value previousScore;
|
|
||||||
std::vector<Move> pv;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::vector<RootMove> RootMoveVector;
|
|
||||||
|
|
||||||
/// The LimitsType struct stores information sent by GUI about available time
|
|
||||||
/// to search the current move, maximum depth/time, if we are in analysis mode
|
|
||||||
/// or if we have to ponder while it's our opponent's turn to move.
|
|
||||||
|
|
||||||
struct LimitsType {
|
|
||||||
|
|
||||||
LimitsType() { // Using memset on a std::vector is undefined behavior
|
|
||||||
nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = movestogo =
|
|
||||||
depth = movetime = mate = infinite = ponder = 0;
|
|
||||||
}
|
|
||||||
bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); }
|
|
||||||
|
|
||||||
std::vector<Move> searchmoves;
|
|
||||||
int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, movetime, mate, infinite, ponder;
|
|
||||||
int64_t nodes;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// The SignalsType struct stores volatile flags updated during the search
|
|
||||||
/// typically in an async fashion e.g. to stop the search by the GUI.
|
|
||||||
|
|
||||||
struct SignalsType {
|
|
||||||
bool stop, stopOnPonderhit, firstRootMove, failedLowAtRoot;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr;
|
|
||||||
|
|
||||||
extern volatile SignalsType Signals;
|
|
||||||
extern LimitsType Limits;
|
|
||||||
extern RootMoveVector RootMoves;
|
|
||||||
extern Position RootPos;
|
|
||||||
extern Time::point SearchTime;
|
|
||||||
extern StateStackPtr SetupStates;
|
|
||||||
|
|
||||||
void init();
|
|
||||||
void think();
|
|
||||||
template<bool Root> uint64_t perft(Position& pos, Depth depth);
|
|
||||||
|
|
||||||
} // namespace Search
|
|
||||||
|
|
||||||
#endif // #ifndef SEARCH_H_INCLUDED
|
|
385
src/thread.cpp
385
src/thread.cpp
|
@ -1,385 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <algorithm> // For std::count
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include "movegen.h"
|
|
||||||
#include "search.h"
|
|
||||||
#include "thread.h"
|
|
||||||
#include "uci.h"
|
|
||||||
|
|
||||||
using namespace Search;
|
|
||||||
|
|
||||||
ThreadPool Threads; // Global object
|
|
||||||
|
|
||||||
extern void check_time();
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// start_routine() is the C function which is called when a new thread
|
|
||||||
// is launched. It is a wrapper to the virtual function idle_loop().
|
|
||||||
|
|
||||||
extern "C" { long start_routine(ThreadBase* th) { th->idle_loop(); return 0; } }
|
|
||||||
|
|
||||||
|
|
||||||
// Helpers to launch a thread after creation and joining before delete. Must be
|
|
||||||
// outside Thread c'tor and d'tor because the object must be fully initialized
|
|
||||||
// when start_routine (and hence virtual idle_loop) is called and when joining.
|
|
||||||
|
|
||||||
template<typename T> T* new_thread() {
|
|
||||||
T* th = new T();
|
|
||||||
thread_create(th->handle, start_routine, th); // Will go to sleep
|
|
||||||
return th;
|
|
||||||
}
|
|
||||||
|
|
||||||
void delete_thread(ThreadBase* th) {
|
|
||||||
|
|
||||||
th->mutex.lock();
|
|
||||||
th->exit = true; // Search must be already finished
|
|
||||||
th->mutex.unlock();
|
|
||||||
|
|
||||||
th->notify_one();
|
|
||||||
thread_join(th->handle); // Wait for thread termination
|
|
||||||
delete th;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ThreadBase::notify_one() wakes up the thread when there is some work to do
|
|
||||||
|
|
||||||
void ThreadBase::notify_one() {
|
|
||||||
|
|
||||||
mutex.lock();
|
|
||||||
sleepCondition.notify_one();
|
|
||||||
mutex.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ThreadBase::wait_for() set the thread to sleep until 'condition' turns true
|
|
||||||
|
|
||||||
void ThreadBase::wait_for(volatile const bool& condition) {
|
|
||||||
|
|
||||||
mutex.lock();
|
|
||||||
while (!condition) sleepCondition.wait(mutex);
|
|
||||||
mutex.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Thread c'tor makes some init but does not launch any execution thread that
|
|
||||||
// will be started only when c'tor returns.
|
|
||||||
|
|
||||||
Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
|
|
||||||
|
|
||||||
searching = false;
|
|
||||||
maxPly = splitPointsSize = 0;
|
|
||||||
activeSplitPoint = NULL;
|
|
||||||
activePosition = NULL;
|
|
||||||
idx = Threads.size(); // Starts from 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the
|
|
||||||
// current active split point, or in some ancestor of the split point.
|
|
||||||
|
|
||||||
bool Thread::cutoff_occurred() const {
|
|
||||||
|
|
||||||
for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parentSplitPoint)
|
|
||||||
if (sp->cutoff)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Thread::available_to() checks whether the thread is available to help the
|
|
||||||
// thread 'master' at a split point. An obvious requirement is that thread must
|
|
||||||
// be idle. With more than two threads, this is not sufficient: If the thread is
|
|
||||||
// the master of some split point, it is only available as a slave to the slaves
|
|
||||||
// which are busy searching the split point at the top of slave's split point
|
|
||||||
// stack (the "helpful master concept" in YBWC terminology).
|
|
||||||
|
|
||||||
bool Thread::available_to(const Thread* master) const {
|
|
||||||
|
|
||||||
if (searching)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Make a local copy to be sure it doesn't become zero under our feet while
|
|
||||||
// testing next condition and so leading to an out of bounds access.
|
|
||||||
const int size = splitPointsSize;
|
|
||||||
|
|
||||||
// No split points means that the thread is available as a slave for any
|
|
||||||
// other thread otherwise apply the "helpful master" concept if possible.
|
|
||||||
return !size || splitPoints[size - 1].slavesMask.test(master->idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Thread::split() does the actual work of distributing the work at a node between
|
|
||||||
// several available threads. If it does not succeed in splitting the node
|
|
||||||
// (because no idle threads are available), the function immediately returns.
|
|
||||||
// If splitting is possible, a SplitPoint object is initialized with all the
|
|
||||||
// data that must be copied to the helper threads and then helper threads are
|
|
||||||
// informed that they have been assigned work. This will cause them to instantly
|
|
||||||
// leave their idle loops and call search(). When all threads have returned from
|
|
||||||
// search() then split() returns.
|
|
||||||
|
|
||||||
void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bestValue,
|
|
||||||
Move* bestMove, Depth depth, int moveCount,
|
|
||||||
MovePicker* movePicker, int nodeType, bool cutNode) {
|
|
||||||
|
|
||||||
assert(searching);
|
|
||||||
assert(-VALUE_INFINITE < *bestValue && *bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE);
|
|
||||||
assert(depth >= Threads.minimumSplitDepth);
|
|
||||||
assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD);
|
|
||||||
|
|
||||||
// Pick and init the next available split point
|
|
||||||
SplitPoint& sp = splitPoints[splitPointsSize];
|
|
||||||
|
|
||||||
sp.masterThread = this;
|
|
||||||
sp.parentSplitPoint = activeSplitPoint;
|
|
||||||
sp.slavesMask = 0, sp.slavesMask.set(idx);
|
|
||||||
sp.depth = depth;
|
|
||||||
sp.bestValue = *bestValue;
|
|
||||||
sp.bestMove = *bestMove;
|
|
||||||
sp.alpha = alpha;
|
|
||||||
sp.beta = beta;
|
|
||||||
sp.nodeType = nodeType;
|
|
||||||
sp.cutNode = cutNode;
|
|
||||||
sp.movePicker = movePicker;
|
|
||||||
sp.moveCount = moveCount;
|
|
||||||
sp.pos = &pos;
|
|
||||||
sp.nodes = 0;
|
|
||||||
sp.cutoff = false;
|
|
||||||
sp.ss = ss;
|
|
||||||
|
|
||||||
// Try to allocate available threads and ask them to start searching setting
|
|
||||||
// 'searching' flag. This must be done under lock protection to avoid concurrent
|
|
||||||
// allocation of the same slave by another master.
|
|
||||||
Threads.mutex.lock();
|
|
||||||
sp.mutex.lock();
|
|
||||||
|
|
||||||
sp.allSlavesSearching = true; // Must be set under lock protection
|
|
||||||
++splitPointsSize;
|
|
||||||
activeSplitPoint = &sp;
|
|
||||||
activePosition = NULL;
|
|
||||||
|
|
||||||
Thread* slave;
|
|
||||||
|
|
||||||
while ((slave = Threads.available_slave(this)) != NULL)
|
|
||||||
{
|
|
||||||
sp.slavesMask.set(slave->idx);
|
|
||||||
slave->activeSplitPoint = &sp;
|
|
||||||
slave->searching = true; // Slave leaves idle_loop()
|
|
||||||
slave->notify_one(); // Could be sleeping
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything is set up. The master thread enters the idle loop, from which
|
|
||||||
// it will instantly launch a search, because its 'searching' flag is set.
|
|
||||||
// The thread will return from the idle loop when all slaves have finished
|
|
||||||
// their work at this split point.
|
|
||||||
sp.mutex.unlock();
|
|
||||||
Threads.mutex.unlock();
|
|
||||||
|
|
||||||
Thread::idle_loop(); // Force a call to base class idle_loop()
|
|
||||||
|
|
||||||
// In the helpful master concept, a master can help only a sub-tree of its
|
|
||||||
// split point and because everything is finished here, it's not possible
|
|
||||||
// for the master to be booked.
|
|
||||||
assert(!searching);
|
|
||||||
assert(!activePosition);
|
|
||||||
|
|
||||||
// We have returned from the idle loop, which means that all threads are
|
|
||||||
// finished. Note that setting 'searching' and decreasing splitPointsSize must
|
|
||||||
// be done under lock protection to avoid a race with Thread::available_to().
|
|
||||||
Threads.mutex.lock();
|
|
||||||
sp.mutex.lock();
|
|
||||||
|
|
||||||
searching = true;
|
|
||||||
--splitPointsSize;
|
|
||||||
activeSplitPoint = sp.parentSplitPoint;
|
|
||||||
activePosition = &pos;
|
|
||||||
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
|
|
||||||
*bestMove = sp.bestMove;
|
|
||||||
*bestValue = sp.bestValue;
|
|
||||||
|
|
||||||
sp.mutex.unlock();
|
|
||||||
Threads.mutex.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TimerThread::idle_loop() is where the timer thread waits Resolution milliseconds
|
|
||||||
// and then calls check_time(). When not searching, thread sleeps until it's woken up.
|
|
||||||
|
|
||||||
void TimerThread::idle_loop() {
|
|
||||||
|
|
||||||
while (!exit)
|
|
||||||
{
|
|
||||||
mutex.lock();
|
|
||||||
|
|
||||||
if (!exit)
|
|
||||||
sleepCondition.wait_for(mutex, run ? Resolution : INT_MAX);
|
|
||||||
|
|
||||||
mutex.unlock();
|
|
||||||
|
|
||||||
if (run)
|
|
||||||
check_time();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MainThread::idle_loop() is where the main thread is parked waiting to be started
|
|
||||||
// when there is a new search. The main thread will launch all the slave threads.
|
|
||||||
|
|
||||||
void MainThread::idle_loop() {
|
|
||||||
|
|
||||||
while (!exit)
|
|
||||||
{
|
|
||||||
mutex.lock();
|
|
||||||
|
|
||||||
thinking = false;
|
|
||||||
|
|
||||||
while (!thinking && !exit)
|
|
||||||
{
|
|
||||||
Threads.sleepCondition.notify_one(); // Wake up the UI thread if needed
|
|
||||||
sleepCondition.wait(mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex.unlock();
|
|
||||||
|
|
||||||
if (!exit)
|
|
||||||
{
|
|
||||||
searching = true;
|
|
||||||
|
|
||||||
Search::think();
|
|
||||||
|
|
||||||
assert(searching);
|
|
||||||
|
|
||||||
searching = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ThreadPool::init() is called at startup to create and launch requested threads,
|
|
||||||
// that will go immediately to sleep. We cannot use a c'tor because Threads is a
|
|
||||||
// static object and we need a fully initialized engine at this point due to
|
|
||||||
// allocation of Endgames in Thread c'tor.
|
|
||||||
|
|
||||||
void ThreadPool::init() {
|
|
||||||
|
|
||||||
timer = new_thread<TimerThread>();
|
|
||||||
push_back(new_thread<MainThread>());
|
|
||||||
read_uci_options();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ThreadPool::exit() terminates the threads before the program exits. Cannot be
|
|
||||||
// done in d'tor because threads must be terminated before freeing us.
|
|
||||||
|
|
||||||
void ThreadPool::exit() {
|
|
||||||
|
|
||||||
delete_thread(timer); // As first because check_time() accesses threads data
|
|
||||||
|
|
||||||
for (iterator it = begin(); it != end(); ++it)
|
|
||||||
delete_thread(*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ThreadPool::read_uci_options() updates internal threads parameters from the
|
|
||||||
// corresponding UCI options and creates/destroys threads to match the requested
|
|
||||||
// number. Thread objects are dynamically allocated to avoid creating all possible
|
|
||||||
// threads in advance (which include pawns and material tables), even if only a
|
|
||||||
// few are to be used.
|
|
||||||
|
|
||||||
void ThreadPool::read_uci_options() {
|
|
||||||
|
|
||||||
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
|
|
||||||
size_t requested = Options["Threads"];
|
|
||||||
|
|
||||||
assert(requested > 0);
|
|
||||||
|
|
||||||
// If zero (default) then set best minimum split depth automatically
|
|
||||||
if (!minimumSplitDepth)
|
|
||||||
minimumSplitDepth = requested < 8 ? 4 * ONE_PLY : 7 * ONE_PLY;
|
|
||||||
|
|
||||||
while (size() < requested)
|
|
||||||
push_back(new_thread<Thread>());
|
|
||||||
|
|
||||||
while (size() > requested)
|
|
||||||
{
|
|
||||||
delete_thread(back());
|
|
||||||
pop_back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ThreadPool::available_slave() tries to find an idle thread which is available
|
|
||||||
// as a slave for the thread 'master'.
|
|
||||||
|
|
||||||
Thread* ThreadPool::available_slave(const Thread* master) const {
|
|
||||||
|
|
||||||
for (const_iterator it = begin(); it != end(); ++it)
|
|
||||||
if ((*it)->available_to(master))
|
|
||||||
return *it;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ThreadPool::wait_for_think_finished() waits for main thread to finish the search
|
|
||||||
|
|
||||||
void ThreadPool::wait_for_think_finished() {
|
|
||||||
|
|
||||||
MainThread* th = main();
|
|
||||||
th->mutex.lock();
|
|
||||||
while (th->thinking) sleepCondition.wait(th->mutex);
|
|
||||||
th->mutex.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ThreadPool::start_thinking() wakes up the main thread sleeping in
|
|
||||||
// MainThread::idle_loop() and starts a new search, then returns immediately.
|
|
||||||
|
|
||||||
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
|
|
||||||
StateStackPtr& states) {
|
|
||||||
wait_for_think_finished();
|
|
||||||
|
|
||||||
SearchTime = Time::now(); // As early as possible
|
|
||||||
|
|
||||||
Signals.stopOnPonderhit = Signals.firstRootMove = false;
|
|
||||||
Signals.stop = Signals.failedLowAtRoot = false;
|
|
||||||
|
|
||||||
RootMoves.clear();
|
|
||||||
RootPos = pos;
|
|
||||||
Limits = limits;
|
|
||||||
if (states.get()) // If we don't set a new position, preserve current state
|
|
||||||
{
|
|
||||||
SetupStates = states; // Ownership transfer here
|
|
||||||
assert(!states.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MoveList<LEGAL> it(pos); *it; ++it)
|
|
||||||
if ( limits.searchmoves.empty()
|
|
||||||
|| std::count(limits.searchmoves.begin(), limits.searchmoves.end(), *it))
|
|
||||||
RootMoves.push_back(RootMove(*it));
|
|
||||||
|
|
||||||
main()->thinking = true;
|
|
||||||
main()->notify_one(); // Starts main thread
|
|
||||||
}
|
|
176
src/thread.h
176
src/thread.h
|
@ -1,176 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef THREAD_H_INCLUDED
|
|
||||||
#define THREAD_H_INCLUDED
|
|
||||||
|
|
||||||
#include <bitset>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "material.h"
|
|
||||||
#include "movepick.h"
|
|
||||||
#include "pawns.h"
|
|
||||||
#include "position.h"
|
|
||||||
#include "search.h"
|
|
||||||
|
|
||||||
const int MAX_THREADS = 128;
|
|
||||||
const int MAX_SPLITPOINTS_PER_THREAD = 8;
|
|
||||||
|
|
||||||
struct Mutex {
|
|
||||||
Mutex() { lock_init(l); }
|
|
||||||
~Mutex() { lock_destroy(l); }
|
|
||||||
|
|
||||||
void lock() { lock_grab(l); }
|
|
||||||
void unlock() { lock_release(l); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend struct ConditionVariable;
|
|
||||||
|
|
||||||
Lock l;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ConditionVariable {
|
|
||||||
ConditionVariable() { cond_init(c); }
|
|
||||||
~ConditionVariable() { cond_destroy(c); }
|
|
||||||
|
|
||||||
void wait(Mutex& m) { cond_wait(c, m.l); }
|
|
||||||
void wait_for(Mutex& m, int ms) { timed_wait(c, m.l, ms); }
|
|
||||||
void notify_one() { cond_signal(c); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
WaitCondition c;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Thread;
|
|
||||||
|
|
||||||
struct SplitPoint {
|
|
||||||
|
|
||||||
// Const data after split point has been setup
|
|
||||||
const Position* pos;
|
|
||||||
Search::Stack* ss;
|
|
||||||
Thread* masterThread;
|
|
||||||
Depth depth;
|
|
||||||
Value beta;
|
|
||||||
int nodeType;
|
|
||||||
bool cutNode;
|
|
||||||
|
|
||||||
// Const pointers to shared data
|
|
||||||
MovePicker* movePicker;
|
|
||||||
SplitPoint* parentSplitPoint;
|
|
||||||
|
|
||||||
// Shared data
|
|
||||||
Mutex mutex;
|
|
||||||
std::bitset<MAX_THREADS> slavesMask;
|
|
||||||
volatile bool allSlavesSearching;
|
|
||||||
volatile uint64_t nodes;
|
|
||||||
volatile Value alpha;
|
|
||||||
volatile Value bestValue;
|
|
||||||
volatile Move bestMove;
|
|
||||||
volatile int moveCount;
|
|
||||||
volatile bool cutoff;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// ThreadBase struct is the base of the hierarchy from where we derive all the
|
|
||||||
/// specialized thread classes.
|
|
||||||
|
|
||||||
struct ThreadBase {
|
|
||||||
|
|
||||||
ThreadBase() : handle(NativeHandle()), exit(false) {}
|
|
||||||
virtual ~ThreadBase() {}
|
|
||||||
virtual void idle_loop() = 0;
|
|
||||||
void notify_one();
|
|
||||||
void wait_for(volatile const bool& b);
|
|
||||||
|
|
||||||
Mutex mutex;
|
|
||||||
ConditionVariable sleepCondition;
|
|
||||||
NativeHandle handle;
|
|
||||||
volatile bool exit;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// Thread struct keeps together all the thread related stuff like locks, state
|
|
||||||
/// and especially split points. 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.
|
|
||||||
|
|
||||||
struct Thread : public ThreadBase {
|
|
||||||
|
|
||||||
Thread();
|
|
||||||
virtual void idle_loop();
|
|
||||||
bool cutoff_occurred() const;
|
|
||||||
bool available_to(const Thread* master) const;
|
|
||||||
|
|
||||||
void split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
|
|
||||||
Depth depth, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode);
|
|
||||||
|
|
||||||
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
|
|
||||||
Material::Table materialTable;
|
|
||||||
Endgames endgames;
|
|
||||||
Pawns::Table pawnsTable;
|
|
||||||
Position* activePosition;
|
|
||||||
size_t idx;
|
|
||||||
int maxPly;
|
|
||||||
SplitPoint* volatile activeSplitPoint;
|
|
||||||
volatile int splitPointsSize;
|
|
||||||
volatile bool searching;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// MainThread and TimerThread are derived classes used to characterize the two
|
|
||||||
/// special threads: the main one and the recurring timer.
|
|
||||||
|
|
||||||
struct MainThread : public Thread {
|
|
||||||
MainThread() : thinking(true) {} // Avoid a race with start_thinking()
|
|
||||||
virtual void idle_loop();
|
|
||||||
volatile bool thinking;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimerThread : public ThreadBase {
|
|
||||||
TimerThread() : run(false) {}
|
|
||||||
virtual void idle_loop();
|
|
||||||
bool run;
|
|
||||||
static const int Resolution = 5; // msec between two check_time() calls
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// ThreadPool struct handles all the threads related stuff like init, starting,
|
|
||||||
/// parking and, most importantly, launching a slave thread at a split point.
|
|
||||||
/// All the access to shared thread data is done through this class.
|
|
||||||
|
|
||||||
struct ThreadPool : public std::vector<Thread*> {
|
|
||||||
|
|
||||||
void init(); // No c'tor and d'tor, threads rely on globals that should
|
|
||||||
void exit(); // be initialized and are valid during the whole thread lifetime.
|
|
||||||
|
|
||||||
MainThread* main() { return static_cast<MainThread*>((*this)[0]); }
|
|
||||||
void read_uci_options();
|
|
||||||
Thread* available_slave(const Thread* master) const;
|
|
||||||
void wait_for_think_finished();
|
|
||||||
void start_thinking(const Position&, const Search::LimitsType&, Search::StateStackPtr&);
|
|
||||||
|
|
||||||
Depth minimumSplitDepth;
|
|
||||||
Mutex mutex;
|
|
||||||
ConditionVariable sleepCondition;
|
|
||||||
TimerThread* timer;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern ThreadPool Threads;
|
|
||||||
|
|
||||||
#endif // #ifndef THREAD_H_INCLUDED
|
|
116
src/timeman.cpp
116
src/timeman.cpp
|
@ -1,116 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cfloat>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include "search.h"
|
|
||||||
#include "timeman.h"
|
|
||||||
#include "uci.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
enum TimeType { OptimumTime, MaxTime };
|
|
||||||
|
|
||||||
const int MoveHorizon = 50; // Plan time management at most this many moves ahead
|
|
||||||
const double MaxRatio = 7.0; // When in trouble, we can step over reserved time with this ratio
|
|
||||||
const double StealRatio = 0.33; // However we must not steal time from remaining moves over this ratio
|
|
||||||
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
double move_importance(int ply) {
|
|
||||||
|
|
||||||
const double XScale = 9.3;
|
|
||||||
const double XShift = 59.8;
|
|
||||||
const double Skew = 0.172;
|
|
||||||
|
|
||||||
return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero
|
|
||||||
}
|
|
||||||
|
|
||||||
template<TimeType T>
|
|
||||||
int remaining(int myTime, int movesToGo, int ply, int slowMover)
|
|
||||||
{
|
|
||||||
const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
|
|
||||||
const double TStealRatio = (T == OptimumTime ? 0 : StealRatio);
|
|
||||||
|
|
||||||
double moveImportance = (move_importance(ply) * slowMover) / 100;
|
|
||||||
double otherMovesImportance = 0;
|
|
||||||
|
|
||||||
for (int i = 1; i < movesToGo; ++i)
|
|
||||||
otherMovesImportance += move_importance(ply + 2 * i);
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
|
|
||||||
/// init() is called at the beginning of the search and calculates the allowed
|
|
||||||
/// thinking time out of the time control and current game ply. We support four
|
|
||||||
/// different kinds of time controls, passed in 'limits':
|
|
||||||
///
|
|
||||||
/// inc == 0 && movestogo == 0 means: x basetime [sudden death!]
|
|
||||||
/// inc == 0 && movestogo != 0 means: x moves in y minutes
|
|
||||||
/// inc > 0 && movestogo == 0 means: x basetime + z increment
|
|
||||||
/// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment
|
|
||||||
|
|
||||||
void TimeManager::init(const Search::LimitsType& limits, Color us, int ply)
|
|
||||||
{
|
|
||||||
int minThinkingTime = Options["Minimum Thinking Time"];
|
|
||||||
int moveOverhead = Options["Move Overhead"];
|
|
||||||
int slowMover = Options["Slow Mover"];
|
|
||||||
|
|
||||||
// Initialize unstablePvFactor to 1 and search times to maximum values
|
|
||||||
unstablePvFactor = 1;
|
|
||||||
optimumSearchTime = maximumSearchTime = std::max(limits.time[us], minThinkingTime);
|
|
||||||
|
|
||||||
const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon;
|
|
||||||
|
|
||||||
// We calculate optimum time usage for different hypothetical "moves to go"-values
|
|
||||||
// and choose the minimum of calculated search time values. Usually the greatest
|
|
||||||
// hypMTG gives the minimum values.
|
|
||||||
for (int hypMTG = 1; hypMTG <= MaxMTG; ++hypMTG)
|
|
||||||
{
|
|
||||||
// Calculate thinking time for hypothetical "moves to go"-value
|
|
||||||
int hypMyTime = limits.time[us]
|
|
||||||
+ limits.inc[us] * (hypMTG - 1)
|
|
||||||
- moveOverhead * (2 + std::min(hypMTG, 40));
|
|
||||||
|
|
||||||
hypMyTime = std::max(hypMyTime, 0);
|
|
||||||
|
|
||||||
int t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, ply, slowMover);
|
|
||||||
int t2 = minThinkingTime + remaining<MaxTime >(hypMyTime, hypMTG, ply, slowMover);
|
|
||||||
|
|
||||||
optimumSearchTime = std::min(t1, optimumSearchTime);
|
|
||||||
maximumSearchTime = std::min(t2, maximumSearchTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Options["Ponder"])
|
|
||||||
optimumSearchTime += optimumSearchTime / 4;
|
|
||||||
|
|
||||||
optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef TIMEMAN_H_INCLUDED
|
|
||||||
#define TIMEMAN_H_INCLUDED
|
|
||||||
|
|
||||||
/// The TimeManager class computes the optimal time to think depending on the
|
|
||||||
/// maximum available time, the game move number and other parameters.
|
|
||||||
|
|
||||||
class TimeManager {
|
|
||||||
public:
|
|
||||||
void init(const Search::LimitsType& limits, Color us, int ply);
|
|
||||||
void pv_instability(double bestMoveChanges) { unstablePvFactor = 1 + bestMoveChanges; }
|
|
||||||
int available_time() const { return int(optimumSearchTime * unstablePvFactor * 0.71); }
|
|
||||||
int maximum_time() const { return maximumSearchTime; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
int optimumSearchTime;
|
|
||||||
int maximumSearchTime;
|
|
||||||
double unstablePvFactor;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // #ifndef TIMEMAN_H_INCLUDED
|
|
96
src/tt.cpp
96
src/tt.cpp
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cstring> // For std::memset
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "bitboard.h"
|
|
||||||
#include "tt.h"
|
|
||||||
|
|
||||||
TranspositionTable TT; // Our global transposition table
|
|
||||||
|
|
||||||
|
|
||||||
/// TranspositionTable::resize() sets the size of the transposition table,
|
|
||||||
/// measured in megabytes. Transposition table consists of a power of 2 number
|
|
||||||
/// of clusters and each cluster consists of TTClusterSize number of TTEntry.
|
|
||||||
|
|
||||||
void TranspositionTable::resize(size_t mbSize) {
|
|
||||||
|
|
||||||
size_t newClusterCount = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(TTCluster));
|
|
||||||
|
|
||||||
if (newClusterCount == clusterCount)
|
|
||||||
return;
|
|
||||||
|
|
||||||
clusterCount = newClusterCount;
|
|
||||||
|
|
||||||
free(mem);
|
|
||||||
mem = calloc(clusterCount * sizeof(TTCluster) + CACHE_LINE_SIZE - 1, 1);
|
|
||||||
|
|
||||||
if (!mem)
|
|
||||||
{
|
|
||||||
std::cerr << "Failed to allocate " << mbSize
|
|
||||||
<< "MB for transposition table." << std::endl;
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
table = (TTCluster*)((uintptr_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// TranspositionTable::clear() overwrites the entire transposition table
|
|
||||||
/// with zeros. It is called whenever the table is resized, or when the
|
|
||||||
/// user asks the program to clear the table (from the UCI interface).
|
|
||||||
|
|
||||||
void TranspositionTable::clear() {
|
|
||||||
|
|
||||||
std::memset(table, 0, clusterCount * sizeof(TTCluster));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// TranspositionTable::probe() looks up the current position in the transposition
|
|
||||||
/// table. It returns true and a pointer to the TTEntry if the position is found.
|
|
||||||
/// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry
|
|
||||||
/// to be replaced later. A TTEntry t1 is considered to be more valuable than a
|
|
||||||
/// TTEntry t2 if t1 is from the current search and t2 is from a previous search,
|
|
||||||
/// or if the depth of t1 is bigger than the depth of t2.
|
|
||||||
|
|
||||||
TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
|
|
||||||
|
|
||||||
TTEntry* const tte = first_entry(key);
|
|
||||||
const uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < TTClusterSize; ++i)
|
|
||||||
if (!tte[i].key16 || tte[i].key16 == key16)
|
|
||||||
{
|
|
||||||
if (tte[i].key16)
|
|
||||||
tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh
|
|
||||||
|
|
||||||
return found = (bool)tte[i].key16, &tte[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find an entry to be replaced according to the replacement strategy
|
|
||||||
TTEntry* replace = tte;
|
|
||||||
for (unsigned i = 1; i < TTClusterSize; ++i)
|
|
||||||
if ( (( tte[i].genBound8 & 0xFC) == generation8 || tte[i].bound() == BOUND_EXACT)
|
|
||||||
- ((replace->genBound8 & 0xFC) == generation8)
|
|
||||||
- (tte[i].depth8 < replace->depth8) < 0)
|
|
||||||
replace = &tte[i];
|
|
||||||
|
|
||||||
return found = false, replace;
|
|
||||||
}
|
|
115
src/tt.h
115
src/tt.h
|
@ -1,115 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef TT_H_INCLUDED
|
|
||||||
#define TT_H_INCLUDED
|
|
||||||
|
|
||||||
#include "misc.h"
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
/// The TTEntry is the 10 bytes transposition table entry, defined as below:
|
|
||||||
///
|
|
||||||
/// key 16 bit
|
|
||||||
/// move 16 bit
|
|
||||||
/// value 16 bit
|
|
||||||
/// eval value 16 bit
|
|
||||||
/// generation 6 bit
|
|
||||||
/// bound type 2 bit
|
|
||||||
/// depth 8 bit
|
|
||||||
|
|
||||||
struct TTEntry {
|
|
||||||
|
|
||||||
Move move() const { return (Move )move16; }
|
|
||||||
Value value() const { return (Value)value16; }
|
|
||||||
Value eval_value() const { return (Value)evalValue; }
|
|
||||||
Depth depth() const { return (Depth)depth8; }
|
|
||||||
Bound bound() const { return (Bound)(genBound8 & 0x3); }
|
|
||||||
|
|
||||||
void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) {
|
|
||||||
|
|
||||||
if (m || (k >> 48) != key16) // Preserve any existing move for the same position
|
|
||||||
move16 = (uint16_t)m;
|
|
||||||
|
|
||||||
key16 = (uint16_t)(k >> 48);
|
|
||||||
value16 = (int16_t)v;
|
|
||||||
evalValue = (int16_t)ev;
|
|
||||||
genBound8 = (uint8_t)(g | b);
|
|
||||||
depth8 = (int8_t)d;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class TranspositionTable;
|
|
||||||
|
|
||||||
uint16_t key16;
|
|
||||||
uint16_t move16;
|
|
||||||
int16_t value16;
|
|
||||||
int16_t evalValue;
|
|
||||||
uint8_t genBound8;
|
|
||||||
int8_t depth8;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// TTCluster is a 32 bytes cluster of TT entries consisting of:
|
|
||||||
///
|
|
||||||
/// 3 x TTEntry (3 x 10 bytes)
|
|
||||||
/// padding (2 bytes)
|
|
||||||
|
|
||||||
static const unsigned TTClusterSize = 3;
|
|
||||||
|
|
||||||
struct TTCluster {
|
|
||||||
TTEntry entry[TTClusterSize];
|
|
||||||
char padding[2];
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A TranspositionTable consists of a power of 2 number of clusters and each
|
|
||||||
/// cluster consists of TTClusterSize number of TTEntry. Each non-empty entry
|
|
||||||
/// contains information of exactly one position. The size of a cluster should
|
|
||||||
/// not be bigger than a cache line size. In case it is less, it should be padded
|
|
||||||
/// to guarantee always aligned accesses.
|
|
||||||
|
|
||||||
class TranspositionTable {
|
|
||||||
|
|
||||||
public:
|
|
||||||
~TranspositionTable() { free(mem); }
|
|
||||||
void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound
|
|
||||||
uint8_t generation() const { return generation8; }
|
|
||||||
TTEntry* probe(const Key key, bool& found) const;
|
|
||||||
TTEntry* first_entry(const Key key) const;
|
|
||||||
void resize(size_t mbSize);
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
private:
|
|
||||||
size_t clusterCount;
|
|
||||||
TTCluster* table;
|
|
||||||
void* mem;
|
|
||||||
uint8_t generation8; // Size must be not bigger than TTEntry::genBound8
|
|
||||||
};
|
|
||||||
|
|
||||||
extern TranspositionTable TT;
|
|
||||||
|
|
||||||
|
|
||||||
/// TranspositionTable::first_entry() returns a pointer to the first entry of
|
|
||||||
/// a cluster given a position. The lowest order bits of the key are used to
|
|
||||||
/// get the index of the cluster inside the table.
|
|
||||||
|
|
||||||
inline TTEntry* TranspositionTable::first_entry(const Key key) const {
|
|
||||||
|
|
||||||
return &table[(size_t)key & (clusterCount - 1)].entry[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // #ifndef TT_H_INCLUDED
|
|
282
src/uci.cpp
282
src/uci.cpp
|
@ -1,282 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "evaluate.h"
|
|
||||||
#include "movegen.h"
|
|
||||||
#include "position.h"
|
|
||||||
#include "search.h"
|
|
||||||
#include "thread.h"
|
|
||||||
#include "tt.h"
|
|
||||||
#include "uci.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
extern void benchmark(const Position& pos, istream& is);
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// FEN string of the initial position, normal chess
|
|
||||||
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
|
||||||
|
|
||||||
// Stack to keep track of the position states along the setup moves (from the
|
|
||||||
// start position to the position just before the search starts). Needed by
|
|
||||||
// 'draw by repetition' detection.
|
|
||||||
Search::StateStackPtr SetupStates;
|
|
||||||
|
|
||||||
|
|
||||||
// position() is called when engine receives the "position" UCI command.
|
|
||||||
// The function sets up the position described in the given FEN string ("fen")
|
|
||||||
// or the starting position ("startpos") and then makes the moves given in the
|
|
||||||
// following move list ("moves").
|
|
||||||
|
|
||||||
void position(Position& pos, istringstream& is) {
|
|
||||||
|
|
||||||
Move m;
|
|
||||||
string token, fen;
|
|
||||||
|
|
||||||
is >> token;
|
|
||||||
|
|
||||||
if (token == "startpos")
|
|
||||||
{
|
|
||||||
fen = StartFEN;
|
|
||||||
is >> token; // Consume "moves" token if any
|
|
||||||
}
|
|
||||||
else if (token == "fen")
|
|
||||||
while (is >> token && token != "moves")
|
|
||||||
fen += token + " ";
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
|
|
||||||
pos.set(fen, Options["UCI_Chess960"], Threads.main());
|
|
||||||
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
|
|
||||||
|
|
||||||
// Parse move list (if any)
|
|
||||||
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
|
|
||||||
{
|
|
||||||
SetupStates->push(StateInfo());
|
|
||||||
pos.do_move(m, SetupStates->top());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// setoption() is called when engine receives the "setoption" UCI command. The
|
|
||||||
// function updates the UCI option ("name") to the given value ("value").
|
|
||||||
|
|
||||||
void setoption(istringstream& is) {
|
|
||||||
|
|
||||||
string token, name, value;
|
|
||||||
|
|
||||||
is >> token; // Consume "name" token
|
|
||||||
|
|
||||||
// Read option name (can contain spaces)
|
|
||||||
while (is >> token && token != "value")
|
|
||||||
name += string(" ", !name.empty()) + token;
|
|
||||||
|
|
||||||
// Read option value (can contain spaces)
|
|
||||||
while (is >> token)
|
|
||||||
value += string(" ", !value.empty()) + token;
|
|
||||||
|
|
||||||
if (Options.count(name))
|
|
||||||
Options[name] = value;
|
|
||||||
else
|
|
||||||
sync_cout << "No such option: " << name << sync_endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// go() is called when engine receives the "go" UCI command. The function sets
|
|
||||||
// the thinking time and other parameters from the input string, then starts
|
|
||||||
// the search.
|
|
||||||
|
|
||||||
void go(const Position& pos, istringstream& is) {
|
|
||||||
|
|
||||||
Search::LimitsType limits;
|
|
||||||
string token;
|
|
||||||
|
|
||||||
while (is >> token)
|
|
||||||
if (token == "searchmoves")
|
|
||||||
while (is >> token)
|
|
||||||
limits.searchmoves.push_back(UCI::to_move(pos, token));
|
|
||||||
|
|
||||||
else if (token == "wtime") is >> limits.time[WHITE];
|
|
||||||
else if (token == "btime") is >> limits.time[BLACK];
|
|
||||||
else if (token == "winc") is >> limits.inc[WHITE];
|
|
||||||
else if (token == "binc") is >> limits.inc[BLACK];
|
|
||||||
else if (token == "movestogo") is >> limits.movestogo;
|
|
||||||
else if (token == "depth") is >> limits.depth;
|
|
||||||
else if (token == "nodes") is >> limits.nodes;
|
|
||||||
else if (token == "movetime") is >> limits.movetime;
|
|
||||||
else if (token == "mate") is >> limits.mate;
|
|
||||||
else if (token == "infinite") limits.infinite = true;
|
|
||||||
else if (token == "ponder") limits.ponder = true;
|
|
||||||
|
|
||||||
Threads.start_thinking(pos, limits, SetupStates);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
|
|
||||||
/// UCI::loop() waits for a command from stdin, parses it and calls the appropriate
|
|
||||||
/// function. Also intercepts EOF from stdin to ensure gracefully exiting if the
|
|
||||||
/// GUI dies unexpectedly. When called with some command line arguments, e.g. to
|
|
||||||
/// run 'bench', once the command is executed the function returns immediately.
|
|
||||||
/// In addition to the UCI ones, also some additional debug commands are supported.
|
|
||||||
|
|
||||||
void UCI::loop(int argc, char* argv[]) {
|
|
||||||
|
|
||||||
Position pos(StartFEN, false, Threads.main()); // The root position
|
|
||||||
string token, cmd;
|
|
||||||
|
|
||||||
for (int i = 1; i < argc; ++i)
|
|
||||||
cmd += std::string(argv[i]) + " ";
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF
|
|
||||||
cmd = "quit";
|
|
||||||
|
|
||||||
istringstream is(cmd);
|
|
||||||
|
|
||||||
token.clear(); // getline() could return empty or blank line
|
|
||||||
is >> skipws >> token;
|
|
||||||
|
|
||||||
// The 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 == "quit"
|
|
||||||
|| token == "stop"
|
|
||||||
|| (token == "ponderhit" && Search::Signals.stopOnPonderhit))
|
|
||||||
{
|
|
||||||
Search::Signals.stop = true;
|
|
||||||
Threads.main()->notify_one(); // Could be sleeping
|
|
||||||
}
|
|
||||||
else if (token == "ponderhit")
|
|
||||||
Search::Limits.ponder = false; // Switch to normal search
|
|
||||||
|
|
||||||
else if (token == "uci")
|
|
||||||
sync_cout << "id name " << engine_info(true)
|
|
||||||
<< "\n" << Options
|
|
||||||
<< "\nuciok" << sync_endl;
|
|
||||||
|
|
||||||
else if (token == "isready") sync_cout << "readyok" << sync_endl;
|
|
||||||
else if (token == "ucinewgame") TT.clear();
|
|
||||||
else if (token == "go") go(pos, is);
|
|
||||||
else if (token == "position") position(pos, is);
|
|
||||||
else if (token == "setoption") setoption(is);
|
|
||||||
|
|
||||||
// Additional custom non-UCI commands, useful for debugging
|
|
||||||
else if (token == "flip") pos.flip();
|
|
||||||
else if (token == "bench") benchmark(pos, is);
|
|
||||||
else if (token == "d") sync_cout << pos << sync_endl;
|
|
||||||
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
|
|
||||||
else if (token == "perft")
|
|
||||||
{
|
|
||||||
int depth;
|
|
||||||
stringstream ss;
|
|
||||||
|
|
||||||
is >> depth;
|
|
||||||
ss << Options["Hash"] << " "
|
|
||||||
<< Options["Threads"] << " " << depth << " current perft";
|
|
||||||
|
|
||||||
benchmark(pos, ss);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
sync_cout << "Unknown command: " << cmd << sync_endl;
|
|
||||||
|
|
||||||
} while (token != "quit" && argc == 1); // Passed args have one-shot behaviour
|
|
||||||
|
|
||||||
Threads.wait_for_think_finished(); // Cannot quit whilst the search is running
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// UCI::value() converts a Value to a string suitable for use with the UCI
|
|
||||||
/// protocol specification:
|
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
|
|
||||||
string UCI::value(Value v) {
|
|
||||||
|
|
||||||
stringstream ss;
|
|
||||||
|
|
||||||
if (abs(v) < VALUE_MATE - MAX_PLY)
|
|
||||||
ss << "cp " << v * 100 / PawnValueEg;
|
|
||||||
else
|
|
||||||
ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.)
|
|
||||||
|
|
||||||
std::string UCI::square(Square s) {
|
|
||||||
|
|
||||||
char sq[] = { char('a' + file_of(s)), char('1' + rank_of(s)), 0 }; // NULL terminated
|
|
||||||
return sq;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q).
|
|
||||||
/// The only special case is castling, where we print in the e1g1 notation in
|
|
||||||
/// normal chess mode, and in e1h1 notation in chess960 mode. Internally all
|
|
||||||
/// castling moves are always encoded as 'king captures rook'.
|
|
||||||
|
|
||||||
string UCI::move(Move m, bool chess960) {
|
|
||||||
|
|
||||||
Square from = from_sq(m);
|
|
||||||
Square to = to_sq(m);
|
|
||||||
|
|
||||||
if (m == MOVE_NONE)
|
|
||||||
return "(none)";
|
|
||||||
|
|
||||||
if (m == MOVE_NULL)
|
|
||||||
return "0000";
|
|
||||||
|
|
||||||
if (type_of(m) == CASTLING && !chess960)
|
|
||||||
to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
|
|
||||||
|
|
||||||
string move = UCI::square(from) + UCI::square(to);
|
|
||||||
|
|
||||||
if (type_of(m) == PROMOTION)
|
|
||||||
move += " pnbrqk"[promotion_type(m)];
|
|
||||||
|
|
||||||
return move;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// UCI::to_move() converts a string representing a move in coordinate notation
|
|
||||||
/// (g1f3, a7a8q) to the corresponding legal Move, if any.
|
|
||||||
|
|
||||||
Move UCI::to_move(const Position& pos, string& str) {
|
|
||||||
|
|
||||||
if (str.length() == 5) // Junior could send promotion piece in uppercase
|
|
||||||
str[4] = char(tolower(str[4]));
|
|
||||||
|
|
||||||
for (MoveList<LEGAL> it(pos); *it; ++it)
|
|
||||||
if (str == UCI::move(*it, pos.is_chess960()))
|
|
||||||
return *it;
|
|
||||||
|
|
||||||
return MOVE_NONE;
|
|
||||||
}
|
|
79
src/uci.h
79
src/uci.h
|
@ -1,79 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef UCIOPTION_H_INCLUDED
|
|
||||||
#define UCIOPTION_H_INCLUDED
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
class Position;
|
|
||||||
|
|
||||||
namespace UCI {
|
|
||||||
|
|
||||||
class Option;
|
|
||||||
|
|
||||||
/// Custom comparator because UCI options should be case insensitive
|
|
||||||
struct CaseInsensitiveLess {
|
|
||||||
bool operator() (const std::string&, const std::string&) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Our options container is actually a std::map
|
|
||||||
typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap;
|
|
||||||
|
|
||||||
/// Option class implements an option as defined by UCI protocol
|
|
||||||
class Option {
|
|
||||||
|
|
||||||
typedef void (*OnChange)(const Option&);
|
|
||||||
|
|
||||||
public:
|
|
||||||
Option(OnChange = NULL);
|
|
||||||
Option(bool v, OnChange = NULL);
|
|
||||||
Option(const char* v, OnChange = NULL);
|
|
||||||
Option(int v, int min, int max, OnChange = NULL);
|
|
||||||
|
|
||||||
Option& operator=(const std::string& v);
|
|
||||||
void operator<<(const Option& o);
|
|
||||||
operator int() const;
|
|
||||||
operator std::string() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
|
|
||||||
|
|
||||||
std::string defaultValue, currentValue, type;
|
|
||||||
int min, max;
|
|
||||||
size_t idx;
|
|
||||||
OnChange on_change;
|
|
||||||
};
|
|
||||||
|
|
||||||
void init(OptionsMap&);
|
|
||||||
void loop(int argc, char* argv[]);
|
|
||||||
|
|
||||||
std::string value(Value v);
|
|
||||||
std::string square(Square s);
|
|
||||||
std::string move(Move m, bool chess960);
|
|
||||||
Move to_move(const Position& pos, std::string& str);
|
|
||||||
|
|
||||||
} // namespace UCI
|
|
||||||
|
|
||||||
extern UCI::OptionsMap Options;
|
|
||||||
|
|
||||||
#endif // #ifndef UCIOPTION_H_INCLUDED
|
|
|
@ -1,160 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "misc.h"
|
|
||||||
#include "thread.h"
|
|
||||||
#include "tt.h"
|
|
||||||
#include "uci.h"
|
|
||||||
#include "syzygy/tbprobe.h"
|
|
||||||
|
|
||||||
using std::string;
|
|
||||||
|
|
||||||
UCI::OptionsMap Options; // Global object
|
|
||||||
|
|
||||||
namespace UCI {
|
|
||||||
|
|
||||||
/// 'On change' actions, triggered by an option's value change
|
|
||||||
void on_clear_hash(const Option&) { TT.clear(); }
|
|
||||||
void on_hash_size(const Option& o) { TT.resize(o); }
|
|
||||||
void on_logger(const Option& o) { start_logger(o); }
|
|
||||||
void on_threads(const Option&) { Threads.read_uci_options(); }
|
|
||||||
void on_tb_path(const Option& o) { Tablebases::init(o); }
|
|
||||||
|
|
||||||
|
|
||||||
/// Our case insensitive less() function as required by UCI protocol
|
|
||||||
bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); }
|
|
||||||
|
|
||||||
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
|
|
||||||
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// init() initializes the UCI options to their hard-coded default values
|
|
||||||
|
|
||||||
void init(OptionsMap& o) {
|
|
||||||
|
|
||||||
o["Write Debug Log"] << Option(false, on_logger);
|
|
||||||
o["Contempt"] << Option(0, -100, 100);
|
|
||||||
o["Min Split Depth"] << Option(0, 0, 12, on_threads);
|
|
||||||
o["Threads"] << Option(1, 1, MAX_THREADS, on_threads);
|
|
||||||
o["Hash"] << Option(16, 1, 1024 * 1024, on_hash_size);
|
|
||||||
o["Clear Hash"] << Option(on_clear_hash);
|
|
||||||
o["Ponder"] << Option(true);
|
|
||||||
o["MultiPV"] << Option(1, 1, 500);
|
|
||||||
o["Skill Level"] << Option(20, 0, 20);
|
|
||||||
o["Move Overhead"] << Option(30, 0, 5000);
|
|
||||||
o["Minimum Thinking Time"] << Option(20, 0, 5000);
|
|
||||||
o["Slow Mover"] << Option(80, 10, 1000);
|
|
||||||
o["UCI_Chess960"] << Option(false);
|
|
||||||
o["SyzygyPath"] << Option("<empty>", on_tb_path);
|
|
||||||
o["SyzygyProbeDepth"] << Option(1, 1, 100);
|
|
||||||
o["Syzygy50MoveRule"] << Option(true);
|
|
||||||
o["SyzygyProbeLimit"] << Option(6, 0, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// operator<<() is used to print all the options default values in chronological
|
|
||||||
/// insertion order (the idx field) and in the format defined by the UCI protocol.
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
|
|
||||||
|
|
||||||
for (size_t idx = 0; idx < om.size(); ++idx)
|
|
||||||
for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it)
|
|
||||||
if (it->second.idx == idx)
|
|
||||||
{
|
|
||||||
const Option& o = it->second;
|
|
||||||
os << "\noption name " << it->first << " type " << o.type;
|
|
||||||
|
|
||||||
if (o.type != "button")
|
|
||||||
os << " default " << o.defaultValue;
|
|
||||||
|
|
||||||
if (o.type == "spin")
|
|
||||||
os << " min " << o.min << " max " << o.max;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Option class constructors and conversion operators
|
|
||||||
|
|
||||||
Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f)
|
|
||||||
{ defaultValue = currentValue = v; }
|
|
||||||
|
|
||||||
Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), on_change(f)
|
|
||||||
{ defaultValue = currentValue = (v ? "true" : "false"); }
|
|
||||||
|
|
||||||
Option::Option(OnChange f) : type("button"), min(0), max(0), on_change(f)
|
|
||||||
{}
|
|
||||||
|
|
||||||
Option::Option(int v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f)
|
|
||||||
{ std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); }
|
|
||||||
|
|
||||||
|
|
||||||
Option::operator int() const {
|
|
||||||
assert(type == "check" || type == "spin");
|
|
||||||
return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
Option::operator std::string() const {
|
|
||||||
assert(type == "string");
|
|
||||||
return currentValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// operator<<() inits options and assigns idx in the correct printing order
|
|
||||||
|
|
||||||
void Option::operator<<(const Option& o) {
|
|
||||||
|
|
||||||
static size_t insert_order = 0;
|
|
||||||
|
|
||||||
*this = o;
|
|
||||||
idx = insert_order++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// operator=() updates currentValue and triggers on_change() action. It's up to
|
|
||||||
/// the GUI to check for option's limits, but we could receive the new value from
|
|
||||||
/// the user by console window, so let's check the bounds anyway.
|
|
||||||
|
|
||||||
Option& Option::operator=(const string& v) {
|
|
||||||
|
|
||||||
assert(!type.empty());
|
|
||||||
|
|
||||||
if ( (type != "button" && v.empty())
|
|
||||||
|| (type == "check" && v != "true" && v != "false")
|
|
||||||
|| (type == "spin" && (atoi(v.c_str()) < min || atoi(v.c_str()) > max)))
|
|
||||||
return *this;
|
|
||||||
|
|
||||||
if (type != "button")
|
|
||||||
currentValue = v;
|
|
||||||
|
|
||||||
if (on_change)
|
|
||||||
on_change(*this);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace UCI
|
|
Loading…
Add table
Reference in a new issue