从 Stockfish 合并 uci/benchmark 等文件
This commit is contained in:
parent
1472f78bc8
commit
60e753be3c
|
@ -21,13 +21,17 @@ INCLUDEPATH += src/test
|
|||
INCLUDEPATH += src/ui/qt
|
||||
|
||||
SOURCES += \
|
||||
src/benchmark.cpp \
|
||||
src/endgame.cpp \
|
||||
src/evaluate.cpp \
|
||||
src/movegen.cpp \
|
||||
src/movepick.cpp \
|
||||
src/thread.cpp \
|
||||
src/trainer.cpp \
|
||||
src/tt.cpp \
|
||||
src/misc.cpp \
|
||||
src/uci.cpp \
|
||||
src/ucioption.cpp \
|
||||
src/zobrist.cpp \
|
||||
src/bitboard.cpp \
|
||||
src/option.cpp \
|
||||
|
@ -54,17 +58,18 @@ HEADERS += \
|
|||
src/evaluate.h \
|
||||
src/movegen.h \
|
||||
src/movepick.h \
|
||||
src/thread.h \
|
||||
src/trainer.h \
|
||||
src/tt.h \
|
||||
src/HashNode.h \
|
||||
src/debug.h \
|
||||
src/hashMap.h \
|
||||
src/misc.h \
|
||||
src/prefetch.h \
|
||||
src/stack.h \
|
||||
src/stopwatch.h \
|
||||
src/aithread.h \
|
||||
src/search.h \
|
||||
src/uci.h \
|
||||
src/zobrist.h \
|
||||
src/bitboard.h \
|
||||
src/option.h \
|
||||
|
|
|
@ -456,18 +456,19 @@
|
|||
<ClInclude Include="src\movegen.h" />
|
||||
<ClInclude Include="src\movepick.h" />
|
||||
<ClInclude Include="src\search.h" />
|
||||
<ClInclude Include="src\timeman.h" />
|
||||
<ClInclude Include="src\trainer.h" />
|
||||
<ClInclude Include="src\tt.h" />
|
||||
<ClInclude Include="src\debug.h" />
|
||||
<ClInclude Include="src\hashmap.h" />
|
||||
<ClInclude Include="src\HashNode.h" />
|
||||
<ClInclude Include="src\misc.h" />
|
||||
<ClInclude Include="src\prefetch.h" />
|
||||
<ClInclude Include="src\stack.h" />
|
||||
<QtMoc Include="src\aithread.h" />
|
||||
<ClInclude Include="src\stopwatch.h" />
|
||||
<ClInclude Include="src\thread.h" />
|
||||
<ClInclude Include="src\thread_win32_osx.h" />
|
||||
<ClInclude Include="src\uci.h" />
|
||||
<ClInclude Include="src\zobrist.h" />
|
||||
<ClInclude Include="src\bitboard.h" />
|
||||
<ClInclude Include="src\option.h" />
|
||||
|
@ -708,16 +709,20 @@
|
|||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\benchmark.cpp" />
|
||||
<ClCompile Include="src\endgame.cpp" />
|
||||
<ClCompile Include="src\evaluate.cpp" />
|
||||
<ClCompile Include="src\movegen.cpp" />
|
||||
<ClCompile Include="src\movepick.cpp" />
|
||||
<ClCompile Include="src\search.cpp" />
|
||||
<ClCompile Include="src\timeman.cpp" />
|
||||
<ClCompile Include="src\trainer.cpp" />
|
||||
<ClCompile Include="src\tt.cpp" />
|
||||
<ClCompile Include="src\misc.cpp" />
|
||||
<ClCompile Include="src\aithread.cpp" />
|
||||
<ClCompile Include="src\thread.cpp" />
|
||||
<ClCompile Include="src\uci.cpp" />
|
||||
<ClCompile Include="src\ucioption.cpp" />
|
||||
<ClCompile Include="src\zobrist.cpp" />
|
||||
<ClCompile Include="src\bitboard.cpp" />
|
||||
<ClCompile Include="src\option.cpp" />
|
||||
|
|
|
@ -90,9 +90,6 @@
|
|||
<ClInclude Include="src\position.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\prefetch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\search.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -129,6 +126,12 @@
|
|||
<ClInclude Include="src\ui\qt\graphicsconst.h">
|
||||
<Filter>Qt Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\uci.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\timeman.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
|
@ -362,6 +365,18 @@
|
|||
<ClCompile Include="src\ui\qt\server.cpp">
|
||||
<Filter>Qt Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\uci.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ucioption.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\timeman.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\benchmark.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="millgame.rc">
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
Fishmill, a UCI Mill Game playing engine derived from Stockfish
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2020 Calcitem <calcitem@outlook.com>
|
||||
|
||||
Fishmill 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.
|
||||
|
||||
Fishmill 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 <fstream>
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <vector>
|
||||
|
||||
#include "position.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
const vector<string> Defaults = {
|
||||
"setoption name UCI_Chess960 value false",
|
||||
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
|
||||
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11",
|
||||
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
|
||||
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14 moves d4e6",
|
||||
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14 moves g2g4",
|
||||
"r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15",
|
||||
"r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13",
|
||||
"r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16",
|
||||
"4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17",
|
||||
"2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11",
|
||||
"r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16",
|
||||
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
|
||||
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
|
||||
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22",
|
||||
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26",
|
||||
"6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1",
|
||||
"3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1",
|
||||
"2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1 moves g5g6 f3e3 g6g5 e3f3",
|
||||
"8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1",
|
||||
"7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1",
|
||||
"8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1",
|
||||
"8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1",
|
||||
"8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1",
|
||||
"8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1",
|
||||
"5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1",
|
||||
"6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1",
|
||||
"1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
|
||||
"6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1",
|
||||
"8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1",
|
||||
"5rk1/q6p/2p3bR/1pPp1rP1/1P1Pp3/P3B1Q1/1K3P2/R7 w - - 93 90",
|
||||
"4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21",
|
||||
"r3k2r/3nnpbp/q2pp1p1/p7/Pp1PPPP1/4BNN1/1P5P/R2Q1RK1 w kq - 0 16",
|
||||
"3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40",
|
||||
"4k3/3q1r2/1N2r1b1/3ppN2/2nPP3/1B1R2n1/2R1Q3/3K4 w - - 5 1",
|
||||
|
||||
// 5-man positions
|
||||
"8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
|
||||
"8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate
|
||||
"8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw
|
||||
|
||||
// 6-man positions
|
||||
"8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate
|
||||
"8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate
|
||||
"8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw
|
||||
|
||||
// 7-man positions
|
||||
"8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw
|
||||
|
||||
// Mate and stalemate positions
|
||||
"6k1/3b3r/1p1p4/p1n2p2/1PPNpP1q/P3Q1p1/1R1RB1P1/5K2 b - - 0 1",
|
||||
"r2r1n2/pp2bk2/2p1p2p/3q4/3PN1QP/2P3R1/P4PP1/5RK1 w - - 0 1",
|
||||
"8/8/8/8/8/6k1/6p1/6K1 w - -",
|
||||
"7k/7P/6K1/8/3B4/8/8/8 b - -",
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/// setup_bench() builds a list of UCI commands to be run by bench. There
|
||||
/// are five parameters: TT size in MB, number of search threads that
|
||||
/// should be used, the limit value spent for each position, a file name
|
||||
/// where to look for positions in FEN format and the type of the limit:
|
||||
/// depth, perft, nodes and movetime (in millisecs).
|
||||
///
|
||||
/// bench -> search default positions up to depth 13
|
||||
/// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB)
|
||||
/// bench 64 4 5000 current movetime -> search current position with 4 threads for 5 sec
|
||||
/// bench 64 1 100000 default nodes -> search default positions for 100K nodes each
|
||||
/// bench 16 1 5 default perft -> run a perft 5 on default positions
|
||||
|
||||
vector<string> setup_bench(Position* current, istream& is) {
|
||||
|
||||
vector<string> fens, list;
|
||||
string go, token;
|
||||
|
||||
// Assign default values to missing arguments
|
||||
string ttSize = (is >> token) ? token : "16";
|
||||
string threads = (is >> token) ? token : "1";
|
||||
string limit = (is >> token) ? token : "13";
|
||||
string fenFile = (is >> token) ? token : "default";
|
||||
string limitType = (is >> token) ? token : "depth";
|
||||
|
||||
go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit;
|
||||
|
||||
if (fenFile == "default")
|
||||
fens = Defaults;
|
||||
|
||||
else if (fenFile == "current")
|
||||
fens.push_back(current->fen());
|
||||
|
||||
else
|
||||
{
|
||||
string fen;
|
||||
ifstream file(fenFile);
|
||||
|
||||
if (!file.is_open())
|
||||
{
|
||||
cerr << "Unable to open file " << fenFile << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while (getline(file, fen))
|
||||
if (!fen.empty())
|
||||
fens.push_back(fen);
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
list.emplace_back("setoption name Threads value " + threads);
|
||||
list.emplace_back("setoption name Hash value " + ttSize);
|
||||
list.emplace_back("ucinewgame");
|
||||
|
||||
for (const string& fen : fens)
|
||||
if (fen.find("setoption") != string::npos)
|
||||
list.emplace_back(fen);
|
||||
else
|
||||
{
|
||||
list.emplace_back("position fen " + fen);
|
||||
list.emplace_back(go);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
102
src/evaluate.cpp
102
src/evaluate.cpp
|
@ -17,7 +17,69 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring> // For std::memset
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "evaluate.h"
|
||||
#include "thread.h"
|
||||
|
||||
namespace Trace
|
||||
{
|
||||
|
||||
enum Tracing
|
||||
{
|
||||
NO_TRACE, TRACE
|
||||
};
|
||||
|
||||
enum Term
|
||||
{ // The first 8 entries are reserved for PieceType
|
||||
MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, INITIATIVE, TOTAL, TERM_NB
|
||||
};
|
||||
|
||||
Score scores[TERM_NB][COLOR_NB];
|
||||
|
||||
double to_cp(Value v)
|
||||
{
|
||||
return double(v) / StoneValue;
|
||||
}
|
||||
|
||||
void add(int idx, Color c, Score s)
|
||||
{
|
||||
scores[idx][c] = s;
|
||||
}
|
||||
|
||||
void add(int idx, Score w, Score b = 0)
|
||||
{
|
||||
scores[idx][WHITE] = w;
|
||||
scores[idx][BLACK] = b;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, Score s)
|
||||
{
|
||||
os << std::setw(5) << to_cp(mg_value(s)) << " "
|
||||
<< std::setw(5) << to_cp(eg_value(s));
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, Term t)
|
||||
{
|
||||
#if 0
|
||||
if (t == MATERIAL || t == IMBALANCE || t == INITIATIVE || t == TOTAL)
|
||||
os << " ---- ----" << " | " << " ---- ----";
|
||||
else
|
||||
os << scores[t][WHITE] << " | " << scores[t][BLACK];
|
||||
|
||||
os << " | " << scores[t][WHITE] - scores[t][BLACK] << "\n";
|
||||
#endif
|
||||
return os;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Trace;
|
||||
|
||||
#ifdef ALPHABETA_AI
|
||||
Value Eval::evaluate(Position *pos)
|
||||
|
@ -113,3 +175,43 @@ Value Eval::evaluate(Position *pos)
|
|||
return value;
|
||||
}
|
||||
#endif // ALPHABETA_AI
|
||||
|
||||
|
||||
|
||||
/// trace() is like evaluate(), but instead of returning a value, it returns
|
||||
/// a string (suitable for outputting to stdout) that contains the detailed
|
||||
/// descriptions and values of each evaluation term. Useful for debugging.
|
||||
|
||||
std::string Eval::trace(Position *pos)
|
||||
{
|
||||
#if 0
|
||||
std::memset(scores, 0, sizeof(scores));
|
||||
|
||||
// TODO
|
||||
//pos->this_thread()->contempt = 0 // TODO: SCORE_ZERO; // Reset any dynamic contempt
|
||||
|
||||
Value v = Evaluation(pos)->value();
|
||||
|
||||
v = pos->side_to_move() == WHITE ? v : -v; // Trace scores are from white's point of view
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
|
||||
<< " Term | White | Black | Total \n"
|
||||
<< " | MG EG | MG EG | MG EG \n"
|
||||
<< " ------------+-------------+-------------+------------\n"
|
||||
<< " Material | " << Term(MATERIAL)
|
||||
<< " Imbalance | " << Term(IMBALANCE)
|
||||
<< " Mobility | " << Term(MOBILITY)
|
||||
<< " Threats | " << Term(THREAT)
|
||||
<< " Passed | " << Term(PASSED)
|
||||
<< " Space | " << Term(SPACE)
|
||||
<< " Initiative | " << Term(INITIATIVE)
|
||||
<< " ------------+-------------+-------------+------------\n"
|
||||
<< " Total | " << Term(TOTAL);
|
||||
|
||||
ss << "\nTotal evaluation: " << to_cp(v) << " (white side)\n";
|
||||
|
||||
return ss.str();
|
||||
#endif
|
||||
return "";
|
||||
}
|
|
@ -22,11 +22,17 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
#include <string>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
class Position;
|
||||
|
||||
namespace Eval {
|
||||
Value evaluate(Position *pos);
|
||||
|
||||
std::string trace(Position *pos);
|
||||
|
||||
Value evaluate(Position *pos);
|
||||
|
||||
#ifdef EVALUATE_ENABLE
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <QFile>
|
||||
#include <iostream>
|
||||
#include "HashNode.h"
|
||||
#include "prefetch.h"
|
||||
#include "misc.h"
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
|
||||
|
@ -131,6 +131,12 @@ namespace CTSL //Concurrent Thread Safe Library
|
|||
#endif
|
||||
}
|
||||
|
||||
void resize(size_t o)
|
||||
{
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
|
||||
//Function to dump the key map to file
|
||||
void dump(const QString &filename)
|
||||
{
|
||||
|
|
486
src/misc.cpp
486
src/misc.cpp
|
@ -1,14 +1,16 @@
|
|||
/*
|
||||
Sanmill, a mill game playing engine derived from NineChess 1.5
|
||||
Copyright (C) 2015-2018 liuweilhy (NineChess author)
|
||||
Copyright (C) 2019-2020 Calcitem <calcitem@outlook.com>
|
||||
Fishmill, a UCI Mill Game playing engine derived from Stockfish
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2020 Calcitem <calcitem@outlook.com>
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
Fishmill 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.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
Fishmill 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.
|
||||
|
@ -17,10 +19,479 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
#ifdef _WIN32
|
||||
#if _WIN32_WINNT < 0x0601
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0601 // Force to include needed API prototypes
|
||||
#endif
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
// The needed Windows API for processor groups could be missed from old Windows
|
||||
// versions, so instead of calling them directly (forcing the linker to resolve
|
||||
// the calls at compile time), try to load them at runtime. To do this we need
|
||||
// first to define the corresponding function pointers.
|
||||
extern "C" {
|
||||
typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP,
|
||||
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD);
|
||||
typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY);
|
||||
typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY *, PGROUP_AFFINITY);
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "misc.h"
|
||||
#include "thread.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/// Version number. If Version is left empty, then compile date in the format
|
||||
/// DD-MM-YY and show in engine_info.
|
||||
const string Version = "";
|
||||
|
||||
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
|
||||
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
|
||||
/// can toggle the logging of std::cout and std:cin at runtime whilst preserving
|
||||
/// usual I/O functionality, all without changing a single line of code!
|
||||
/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
|
||||
|
||||
struct Tie : public streambuf
|
||||
{ // MSVC requires split streambuf for cin and cout
|
||||
|
||||
Tie(streambuf *b, streambuf *l) : buf(b), logBuf(l)
|
||||
{
|
||||
}
|
||||
|
||||
int sync() override
|
||||
{
|
||||
return logBuf->pubsync(), buf->pubsync();
|
||||
}
|
||||
int overflow(int c) override
|
||||
{
|
||||
return log(buf->sputc((char)c), "<< ");
|
||||
}
|
||||
int underflow() override
|
||||
{
|
||||
return buf->sgetc();
|
||||
}
|
||||
int uflow() override
|
||||
{
|
||||
return log(buf->sbumpc(), ">> ");
|
||||
}
|
||||
|
||||
streambuf *buf, *logBuf;
|
||||
|
||||
int log(int c, const char *prefix)
|
||||
{
|
||||
|
||||
static int last = '\n'; // Single log file
|
||||
|
||||
if (last == '\n')
|
||||
logBuf->sputn(prefix, 3);
|
||||
|
||||
return last = logBuf->sputc((char)c);
|
||||
}
|
||||
};
|
||||
|
||||
class Logger
|
||||
{
|
||||
|
||||
Logger() : in(cin.rdbuf(), file.rdbuf()), out(cout.rdbuf(), file.rdbuf())
|
||||
{
|
||||
}
|
||||
~Logger()
|
||||
{
|
||||
start("");
|
||||
}
|
||||
|
||||
ofstream file;
|
||||
Tie in, out;
|
||||
|
||||
public:
|
||||
static void start(const std::string &fname)
|
||||
{
|
||||
|
||||
static Logger l;
|
||||
|
||||
if (!fname.empty() && !l.file.is_open()) {
|
||||
l.file.open(fname, ifstream::out);
|
||||
|
||||
if (!l.file.is_open()) {
|
||||
cerr << "Unable to open debug log file " << fname << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
cin.rdbuf(&l.in);
|
||||
cout.rdbuf(&l.out);
|
||||
} else if (fname.empty() && l.file.is_open()) {
|
||||
cout.rdbuf(l.out.buf);
|
||||
cin.rdbuf(l.in.buf);
|
||||
l.file.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/// engine_info() returns the full name of the current Fishmill version. This
|
||||
/// will be either "Fishmill <Tag> DD-MM-YY" (where DD-MM-YY is the date when
|
||||
/// the program was compiled) or "Fishmill <Version>", depending on whether
|
||||
/// Version is empty.
|
||||
|
||||
const string engine_info(bool to_uci)
|
||||
{
|
||||
|
||||
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
|
||||
string month, day, year;
|
||||
stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008"
|
||||
|
||||
ss << "Fishmill " << Version << setfill('0');
|
||||
|
||||
if (Version.empty()) {
|
||||
date >> month >> day >> year;
|
||||
ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
|
||||
}
|
||||
|
||||
ss << (Is64Bit ? " 64" : "")
|
||||
<< (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : ""))
|
||||
<< (to_uci ? "\nid author " : " by ")
|
||||
<< "Calcitem Team, T. Romstad, M. Costalba, J. Kiiski, G. Linscott";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
/// compiler_info() returns a string trying to describe the compiler we use
|
||||
|
||||
const std::string compiler_info()
|
||||
{
|
||||
|
||||
#define stringify2(x) #x
|
||||
#define stringify(x) stringify2(x)
|
||||
#define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch)
|
||||
|
||||
/// Predefined macros hell:
|
||||
///
|
||||
/// __GNUC__ Compiler is gcc, Clang or Intel on Linux
|
||||
/// __INTEL_COMPILER Compiler is Intel
|
||||
/// _MSC_VER Compiler is MSVC or Intel on Windows
|
||||
/// _WIN32 Building on Windows (any)
|
||||
/// _WIN64 Building on Windows 64 bit
|
||||
|
||||
std::string compiler = "\nCompiled by ";
|
||||
|
||||
#ifdef __clang__
|
||||
compiler += "clang++ ";
|
||||
compiler += make_version_string(__clang_major__, __clang_minor__, __clang_patchlevel__);
|
||||
#elif __INTEL_COMPILER
|
||||
compiler += "Intel compiler ";
|
||||
compiler += "(version ";
|
||||
compiler += stringify(__INTEL_COMPILER) " update " stringify(__INTEL_COMPILER_UPDATE);
|
||||
compiler += ")";
|
||||
#elif _MSC_VER
|
||||
compiler += "MSVC ";
|
||||
compiler += "(version ";
|
||||
compiler += stringify(_MSC_FULL_VER) "." stringify(_MSC_BUILD);
|
||||
compiler += ")";
|
||||
#elif __GNUC__
|
||||
compiler += "g++ (GNUC) ";
|
||||
compiler += make_version_string(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
|
||||
#else
|
||||
compiler += "Unknown compiler ";
|
||||
compiler += "(unknown version)";
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
compiler += " on Apple";
|
||||
#elif defined(__CYGWIN__)
|
||||
compiler += " on Cygwin";
|
||||
#elif defined(__MINGW64__)
|
||||
compiler += " on MinGW64";
|
||||
#elif defined(__MINGW32__)
|
||||
compiler += " on MinGW32";
|
||||
#elif defined(__ANDROID__)
|
||||
compiler += " on Android";
|
||||
#elif defined(__linux__)
|
||||
compiler += " on Linux";
|
||||
#elif defined(_WIN64)
|
||||
compiler += " on Microsoft Windows 64-bit";
|
||||
#elif defined(_WIN32)
|
||||
compiler += " on Microsoft Windows 32-bit";
|
||||
#else
|
||||
compiler += " on unknown system";
|
||||
#endif
|
||||
|
||||
compiler += "\n __VERSION__ macro expands to: ";
|
||||
#ifdef __VERSION__
|
||||
compiler += __VERSION__;
|
||||
#else
|
||||
compiler += "(undefined macro)";
|
||||
#endif
|
||||
compiler += "\n";
|
||||
|
||||
return compiler;
|
||||
}
|
||||
|
||||
|
||||
/// Debug functions used mainly to collect run-time statistics
|
||||
static std::atomic<int64_t> hits[2], means[2];
|
||||
|
||||
void dbg_hit_on(bool b)
|
||||
{
|
||||
++hits[0]; if (b) ++hits[1];
|
||||
}
|
||||
void dbg_hit_on(bool c, bool b)
|
||||
{
|
||||
if (c) dbg_hit_on(b);
|
||||
}
|
||||
void dbg_mean_of(int v)
|
||||
{
|
||||
++means[0]; means[1] += v;
|
||||
}
|
||||
|
||||
void dbg_print()
|
||||
{
|
||||
|
||||
if (hits[0])
|
||||
cerr << "Total " << hits[0] << " Hits " << hits[1]
|
||||
<< " hit rate (%) " << 100 * hits[1] / hits[0] << endl;
|
||||
|
||||
if (means[0])
|
||||
cerr << "Total " << means[0] << " Mean "
|
||||
<< (double)means[1] / means[0] << endl;
|
||||
}
|
||||
|
||||
|
||||
/// Used to serialize access to std::cout to avoid multiple threads writing at
|
||||
/// the same time.
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, SyncCout sc)
|
||||
{
|
||||
|
||||
static std::mutex m;
|
||||
|
||||
if (sc == IO_LOCK)
|
||||
m.lock();
|
||||
|
||||
if (sc == IO_UNLOCK)
|
||||
m.unlock();
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
/// Trampoline helper to avoid moving Logger to misc.h
|
||||
void start_logger(const std::string &fname)
|
||||
{
|
||||
Logger::start(fname);
|
||||
}
|
||||
|
||||
|
||||
/// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking
|
||||
/// function that doesn't stall the CPU waiting for data to be loaded from memory,
|
||||
/// which can be quite slow.
|
||||
#ifdef NO_PREFETCH
|
||||
|
||||
void prefetch(void *)
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void prefetch(void *addr)
|
||||
{
|
||||
|
||||
# if defined(__INTEL_COMPILER)
|
||||
// This hack prevents prefetches from being optimized away by
|
||||
// Intel compiler. Both MSVC and gcc seem not be affected by this.
|
||||
__asm__("");
|
||||
# endif
|
||||
|
||||
# if defined(__INTEL_COMPILER) || defined(_MSC_VER)
|
||||
_mm_prefetch((char *)addr, _MM_HINT_T0);
|
||||
# else
|
||||
__builtin_prefetch(addr);
|
||||
# endif
|
||||
}
|
||||
|
||||
#ifndef PREFETCH_STRIDE
|
||||
/* L1 cache line size */
|
||||
#define L1_CACHE_SHIFT 7
|
||||
#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT)
|
||||
|
||||
#define PREFETCH_STRIDE (4 * L1_CACHE_BYTES)
|
||||
#endif
|
||||
|
||||
void prefetch_range(void *addr, size_t len)
|
||||
{
|
||||
char *cp;
|
||||
char *end = (char *)addr + len;
|
||||
|
||||
for (cp = (char *)addr; cp < end; cp += PREFETCH_STRIDE)
|
||||
prefetch(cp);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// aligned_ttmem_alloc will return suitably aligned memory, and if possible use large pages.
|
||||
/// The returned pointer is the aligned one, while the mem argument is the one that needs to be passed to free.
|
||||
/// With c++17 some of this functionality can be simplified.
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
|
||||
void *aligned_ttmem_alloc(size_t allocSize, void *&mem)
|
||||
{
|
||||
|
||||
constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page sizes
|
||||
size_t size = ((allocSize + alignment - 1) / alignment) * alignment; // multiple of alignment
|
||||
if (posix_memalign(&mem, alignment, size))
|
||||
mem = nullptr;
|
||||
madvise(mem, allocSize, MADV_HUGEPAGE);
|
||||
return mem;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void *aligned_ttmem_alloc(size_t allocSize, void *&mem)
|
||||
{
|
||||
|
||||
constexpr size_t alignment = 64; // assumed cache line size
|
||||
size_t size = allocSize + alignment - 1; // allocate some extra space
|
||||
mem = malloc(size);
|
||||
void *ret = reinterpret_cast<void *>((uintptr_t(mem) + alignment - 1) & ~uintptr_t(alignment - 1));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace WinProcGroup
|
||||
{
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
void bindThisThread(size_t)
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/// best_group() retrieves logical processor information using Windows specific
|
||||
/// API and returns the best group id for the thread with index idx. Original
|
||||
/// code from Texel by Peter Österlund.
|
||||
|
||||
int best_group(size_t idx)
|
||||
{
|
||||
|
||||
int threads = 0;
|
||||
int nodes = 0;
|
||||
int cores = 0;
|
||||
DWORD returnLength = 0;
|
||||
DWORD byteOffset = 0;
|
||||
|
||||
// Early exit if the needed API is not available at runtime
|
||||
HMODULE k32 = GetModuleHandle(L"Kernel32.dll");
|
||||
auto fun1 = (fun1_t)(void(*)())GetProcAddress(k32, "GetLogicalProcessorInformationEx");
|
||||
if (!fun1)
|
||||
return -1;
|
||||
|
||||
// First call to get returnLength. We expect it to fail due to null buffer
|
||||
if (fun1(RelationAll, nullptr, &returnLength))
|
||||
return -1;
|
||||
|
||||
// Once we know returnLength, allocate the buffer
|
||||
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr;
|
||||
ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)malloc(returnLength);
|
||||
|
||||
// Second call, now we expect to succeed
|
||||
if (!fun1(RelationAll, buffer, &returnLength)) {
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (byteOffset < returnLength) {
|
||||
if (ptr->Relationship == RelationNumaNode)
|
||||
nodes++;
|
||||
|
||||
else if (ptr->Relationship == RelationProcessorCore) {
|
||||
cores++;
|
||||
threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1;
|
||||
}
|
||||
|
||||
assert(ptr->Size);
|
||||
byteOffset += ptr->Size;
|
||||
ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)(((char *)ptr) + ptr->Size);
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
||||
std::vector<int> groups;
|
||||
|
||||
// Run as many threads as possible on the same node until core limit is
|
||||
// reached, then move on filling the next node.
|
||||
for (int n = 0; n < nodes; n++)
|
||||
for (int i = 0; i < cores / nodes; i++)
|
||||
groups.push_back(n);
|
||||
|
||||
// In case a core has more than one logical processor (we assume 2) and we
|
||||
// have still threads to allocate, then spread them evenly across available
|
||||
// nodes.
|
||||
for (int t = 0; t < threads - cores; t++)
|
||||
groups.push_back(t % nodes);
|
||||
|
||||
// If we still have more threads than the total number of logical processors
|
||||
// then return -1 and let the OS to decide what to do.
|
||||
return idx < groups.size() ? groups[idx] : -1;
|
||||
}
|
||||
|
||||
|
||||
/// bindThisThread() set the group affinity of the current thread
|
||||
|
||||
void bindThisThread(size_t idx)
|
||||
{
|
||||
|
||||
// Use only local variables to be thread-safe
|
||||
int group = best_group(idx);
|
||||
|
||||
if (group == -1)
|
||||
return;
|
||||
|
||||
// Early exit if the needed API are not available at runtime
|
||||
HMODULE k32 = GetModuleHandle(L"Kernel32.dll");
|
||||
auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx");
|
||||
auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity");
|
||||
|
||||
if (!fun2 || !fun3)
|
||||
return;
|
||||
|
||||
GROUP_AFFINITY affinity;
|
||||
if (fun2((USHORT)group, &affinity))
|
||||
fun3(GetCurrentThread(), &affinity, nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace WinProcGroup
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
QString getAppFileName()
|
||||
{
|
||||
|
@ -30,4 +501,3 @@ QString getAppFileName()
|
|||
|
||||
return filename;
|
||||
}
|
||||
|
||||
|
|
138
src/misc.h
138
src/misc.h
|
@ -1,14 +1,16 @@
|
|||
/*
|
||||
Sanmill, a mill game playing engine derived from NineChess 1.5
|
||||
Copyright (C) 2015-2018 liuweilhy (NineChess author)
|
||||
Copyright (C) 2019-2020 Calcitem <calcitem@outlook.com>
|
||||
Fishmill, a UCI Mill Game playing engine derived from Stockfish
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2020 Calcitem <calcitem@outlook.com>
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
Fishmill 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.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
Fishmill 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.
|
||||
|
@ -17,15 +19,32 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MISC_H
|
||||
#define MISC_H
|
||||
#ifndef MISC_H_INCLUDED
|
||||
#define MISC_H_INCLUDED
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QString>
|
||||
|
||||
using TimePoint = std::chrono::milliseconds::rep; // A value in milliseconds
|
||||
#include "types.h"
|
||||
|
||||
const std::string engine_info(bool to_uci = false);
|
||||
const std::string compiler_info();
|
||||
void prefetch(void *addr);
|
||||
void prefetch_range(void *addr, size_t len);
|
||||
void start_logger(const std::string &fname);
|
||||
void *aligned_ttmem_alloc(size_t size, void *&mem);
|
||||
|
||||
void dbg_hit_on(bool b);
|
||||
void dbg_hit_on(bool c, bool b);
|
||||
void dbg_mean_of(int v);
|
||||
void dbg_print();
|
||||
|
||||
typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds
|
||||
|
||||
static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits");
|
||||
|
||||
|
@ -35,6 +54,103 @@ inline TimePoint now()
|
|||
(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
template<class Entry, int Size>
|
||||
struct HashTable
|
||||
{
|
||||
Entry *operator[](Key key)
|
||||
{
|
||||
return &table[(uint32_t)key & (Size - 1)];
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Entry> table = std::vector<Entry>(Size); // Allocate on the heap
|
||||
};
|
||||
|
||||
|
||||
enum SyncCout
|
||||
{
|
||||
IO_LOCK, IO_UNLOCK
|
||||
};
|
||||
std::ostream &operator<<(std::ostream &, SyncCout);
|
||||
|
||||
#define sync_cout std::cout << IO_LOCK
|
||||
#define sync_endl std::endl << IO_UNLOCK
|
||||
|
||||
namespace Utility
|
||||
{
|
||||
|
||||
/// Clamp a value between lo and hi. Available in c++17.
|
||||
template<class T> constexpr const T &clamp(const T &v, const T &lo, const T &hi)
|
||||
{
|
||||
return v < lo ? lo : v > hi ? hi : v;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// xorshift64star Pseudo-Random Number Generator
|
||||
/// This class is based on original code written and dedicated
|
||||
/// to the public domain by Sebastiano Vigna (2014).
|
||||
/// It has the following characteristics:
|
||||
///
|
||||
/// - Outputs 64-bit numbers
|
||||
/// - Passes Dieharder and SmallCrush test batteries
|
||||
/// - Does not require warm-up, no zeroland to escape
|
||||
/// - Internal state is a single 64-bit integer
|
||||
/// - Period is 2^64 - 1
|
||||
/// - Speed: 1.60 ns/call (Core i7 @3.40GHz)
|
||||
///
|
||||
/// For further analysis see
|
||||
/// <http://vigna.di.unimi.it/ftp/papers/xorshift.pdf>
|
||||
|
||||
class PRNG
|
||||
{
|
||||
|
||||
uint64_t s;
|
||||
|
||||
uint64_t rand64()
|
||||
{
|
||||
|
||||
s ^= s >> 12, s ^= s << 25, s ^= s >> 27;
|
||||
return s * 2685821657736338717LL;
|
||||
}
|
||||
|
||||
public:
|
||||
PRNG(uint64_t seed) : s(seed)
|
||||
{
|
||||
assert(seed);
|
||||
}
|
||||
|
||||
template<typename T> T rand()
|
||||
{
|
||||
return T(rand64());
|
||||
}
|
||||
|
||||
/// Special generator used to fast init magic numbers.
|
||||
/// Output values only have 1/8th of their bits set on average.
|
||||
template<typename T> T sparse_rand()
|
||||
{
|
||||
return T(rand64() & rand64() & rand64());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Under Windows it is not possible for a process to run on more than one
|
||||
/// logical processor group. This usually means to be limited to use max 64
|
||||
/// cores. To overcome this, some special platform specific API should be
|
||||
/// called to set group affinity for each thread. Original code from Texel by
|
||||
/// Peter Österlund.
|
||||
|
||||
namespace WinProcGroup
|
||||
{
|
||||
void bindThisThread(size_t idx);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
using TimePoint = std::chrono::milliseconds::rep; // A value in milliseconds
|
||||
|
||||
static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits");
|
||||
|
||||
inline uint64_t rand64()
|
||||
{
|
||||
return static_cast<uint64_t>(rand()) ^
|
||||
|
@ -51,4 +167,6 @@ inline uint64_t rand56()
|
|||
|
||||
extern QString getAppFileName();
|
||||
|
||||
#endif /* MISC_H */
|
||||
void start_logger(const std::string &fname);
|
||||
|
||||
#endif // #ifndef MISC_H_INCLUDED
|
|
@ -87,10 +87,9 @@ public:
|
|||
(Move)24, (Move)25, (Move)26, (Move)27, (Move)28, (Move)29, (Move)30, (Move)31,
|
||||
};
|
||||
|
||||
//explicit MoveList(const Position &tmppos) : last(generate<T>(tmppos, moveList))
|
||||
// explicit MoveList(const Position &tmppos) : last(generate(tmppos, moveList))
|
||||
// {
|
||||
// }
|
||||
explicit MoveList(Position *pos) : last(generate(pos, moveList))
|
||||
{
|
||||
}
|
||||
|
||||
const ExtMove *begin() const
|
||||
{
|
||||
|
|
|
@ -19,49 +19,49 @@
|
|||
|
||||
#include "option.h"
|
||||
|
||||
Options gameOptions;
|
||||
GameOptions gameOptions;
|
||||
|
||||
void Options::setAutoRestart(bool enabled)
|
||||
void GameOptions::setAutoRestart(bool enabled)
|
||||
{
|
||||
isAutoRestart = enabled;
|
||||
};
|
||||
|
||||
bool Options::getAutoRestart()
|
||||
bool GameOptions::getAutoRestart()
|
||||
{
|
||||
return isAutoRestart;
|
||||
}
|
||||
|
||||
void Options::setAutoChangeFirstMove(bool enabled)
|
||||
void GameOptions::setAutoChangeFirstMove(bool enabled)
|
||||
{
|
||||
isAutoChangeFirstMove = enabled;
|
||||
}
|
||||
|
||||
bool Options::getAutoChangeFirstMove()
|
||||
bool GameOptions::getAutoChangeFirstMove()
|
||||
{
|
||||
return isAutoChangeFirstMove;
|
||||
}
|
||||
|
||||
void Options::setGiveUpIfMostLose(bool enabled)
|
||||
void GameOptions::setGiveUpIfMostLose(bool enabled)
|
||||
{
|
||||
giveUpIfMostLose = enabled;
|
||||
}
|
||||
|
||||
bool Options::getGiveUpIfMostLose()
|
||||
bool GameOptions::getGiveUpIfMostLose()
|
||||
{
|
||||
return giveUpIfMostLose;
|
||||
}
|
||||
|
||||
void Options::setRandomMoveEnabled(bool enabled)
|
||||
void GameOptions::setRandomMoveEnabled(bool enabled)
|
||||
{
|
||||
randomMoveEnabled = enabled;
|
||||
}
|
||||
|
||||
bool Options::getRandomMoveEnabled()
|
||||
bool GameOptions::getRandomMoveEnabled()
|
||||
{
|
||||
return randomMoveEnabled;
|
||||
}
|
||||
|
||||
void Options::setLearnEndgameEnabled(bool enabled)
|
||||
void GameOptions::setLearnEndgameEnabled(bool enabled)
|
||||
{
|
||||
#ifdef ENDGAME_LEARNING_FORCE
|
||||
learnEndgame = true;
|
||||
|
@ -70,7 +70,7 @@ void Options::setLearnEndgameEnabled(bool enabled)
|
|||
#endif
|
||||
}
|
||||
|
||||
bool Options::getLearnEndgameEnabled()
|
||||
bool GameOptions::getLearnEndgameEnabled()
|
||||
{
|
||||
#ifdef ENDGAME_LEARNING_FORCE
|
||||
return true;
|
||||
|
@ -79,36 +79,36 @@ bool Options::getLearnEndgameEnabled()
|
|||
#endif
|
||||
}
|
||||
|
||||
void Options::setIDSEnabled(bool enabled)
|
||||
void GameOptions::setIDSEnabled(bool enabled)
|
||||
{
|
||||
IDSEnabled = enabled;
|
||||
}
|
||||
|
||||
bool Options::getIDSEnabled()
|
||||
bool GameOptions::getIDSEnabled()
|
||||
{
|
||||
return IDSEnabled;
|
||||
}
|
||||
|
||||
// DepthExtension
|
||||
|
||||
void Options::setDepthExtension(bool enabled)
|
||||
void GameOptions::setDepthExtension(bool enabled)
|
||||
{
|
||||
depthExtension = enabled;
|
||||
}
|
||||
|
||||
bool Options::getDepthExtension()
|
||||
bool GameOptions::getDepthExtension()
|
||||
{
|
||||
return depthExtension;
|
||||
}
|
||||
|
||||
// OpeningBook
|
||||
|
||||
void Options::setOpeningBook(bool enabled)
|
||||
void GameOptions::setOpeningBook(bool enabled)
|
||||
{
|
||||
openingBook = enabled;
|
||||
}
|
||||
|
||||
bool Options::getOpeningBook()
|
||||
bool GameOptions::getOpeningBook()
|
||||
{
|
||||
return openingBook;
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
class Options
|
||||
class GameOptions
|
||||
{
|
||||
public:
|
||||
void setAutoRestart(bool enabled);
|
||||
|
@ -68,6 +68,6 @@ private:
|
|||
bool openingBook { false };
|
||||
};
|
||||
|
||||
extern Options gameOptions;
|
||||
extern GameOptions gameOptions;
|
||||
|
||||
#endif /* OPTION_H */
|
||||
|
|
193
src/position.cpp
193
src/position.cpp
|
@ -18,14 +18,22 @@
|
|||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cassert>
|
||||
#include <cstddef> // For offsetof()
|
||||
#include <cstring> // For std::memset, std::memcmp
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "search.h"
|
||||
#include "bitboard.h"
|
||||
#include "misc.h"
|
||||
#include "movegen.h"
|
||||
#include "position.h"
|
||||
#include "thread.h"
|
||||
#include "tt.h"
|
||||
#include "uci.h"
|
||||
|
||||
#include "option.h"
|
||||
#include "zobrist.h"
|
||||
#include "bitboard.h"
|
||||
#include "prefetch.h"
|
||||
|
||||
string tips;
|
||||
|
||||
|
@ -50,6 +58,175 @@ Position::~Position()
|
|||
cmdlist.clear();
|
||||
}
|
||||
|
||||
/// Position::set() initializes the position object with the given FEN string.
|
||||
/// This function is not very robust - make sure that input FENs are correct,
|
||||
/// this is assumed to be the responsibility of the GUI.
|
||||
|
||||
Position &Position::set(const string &fenStr, StateInfo *si, Thread *th)
|
||||
{
|
||||
// TODO
|
||||
#if 0
|
||||
/*
|
||||
A FEN string defines a particular position using only the ASCII character set.
|
||||
|
||||
A FEN string contains six fields separated by a space. The fields are:
|
||||
|
||||
1) Piece placement (from white's perspective). Each rank is described, starting
|
||||
with rank 8 and ending with rank 1. Within each rank, the contents of each
|
||||
square are described from file A through file H. Following the Standard
|
||||
Algebraic Notation (SAN), each piece is identified by a single letter taken
|
||||
from the standard English names. White pieces are designated using upper-case
|
||||
letters ("PNBRQK") whilst Black uses lowercase ("pnbrqk"). Blank squares are
|
||||
noted using digits 1 through 8 (the number of blank squares), and "/"
|
||||
separates ranks.
|
||||
|
||||
2) Active color. "w" means white moves next, "b" means black.
|
||||
|
||||
4) En passant target square (in algebraic notation). If there's no en passant
|
||||
target square, this is "-". If a pawn has just made a 2-square move, this
|
||||
is the position "behind" the pawn. This is recorded only if there is a pawn
|
||||
in position to make an en passant capture, and if there really is a pawn
|
||||
that might have advanced two squares.
|
||||
|
||||
5) Halfmove clock. This is the number of halfmoves since the last pawn advance
|
||||
or capture. This is used to determine if a draw can be claimed under the
|
||||
fifty-move rule.
|
||||
|
||||
6) Fullmove number. The number of the full move. It starts at 1, and is
|
||||
incremented after Black's move.
|
||||
*/
|
||||
|
||||
unsigned char token;
|
||||
size_t idx;
|
||||
Square sq = SQ_A8;
|
||||
std::istringstream ss(fenStr);
|
||||
|
||||
std::memset(this, 0, sizeof(Position));
|
||||
std::memset(si, 0, sizeof(StateInfo));
|
||||
std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE);
|
||||
st = si;
|
||||
|
||||
ss >> std::noskipws;
|
||||
|
||||
// 1. Piece placement
|
||||
while ((ss >> token) && !isspace(token)) {
|
||||
if (isdigit(token))
|
||||
sq += (token - '0') * EAST; // Advance the given number of files
|
||||
|
||||
else if (token == '/')
|
||||
sq += 2 * SOUTH;
|
||||
|
||||
else if ((idx = PieceToChar.find(token)) != string::npos) {
|
||||
put_piece(Piece(idx), sq);
|
||||
++sq;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Active color
|
||||
ss >> token;
|
||||
sideToMove = (token == 'w' ? WHITE : BLACK);
|
||||
ss >> token;
|
||||
|
||||
// 5-6. Halfmove clock and fullmove number
|
||||
ss >> std::skipws >> st->rule50 >> gamePly;
|
||||
|
||||
// Convert from fullmove starting from 1 to gamePly starting from 0,
|
||||
// handle also common incorrect FEN with fullmove = 0.
|
||||
gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
|
||||
|
||||
thisThread = th;
|
||||
set_state(st);
|
||||
|
||||
assert(pos_is_ok());
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Position::set_state() computes the hash keys of the position, and other
|
||||
/// data that once computed is updated incrementally as moves are made.
|
||||
/// The function is only used when a new position is set up, and to verify
|
||||
/// the correctness of the StateInfo data when running in debug mode.
|
||||
|
||||
void Position::set_state(StateInfo *si) const
|
||||
{
|
||||
// TODO
|
||||
#if 0
|
||||
si->key = 0;
|
||||
|
||||
for (Bitboard b = pieces(); b; ) {
|
||||
Square s = pop_lsb(&b);
|
||||
Piece pc = piece_on(s);
|
||||
si->key ^= Zobrist::psq[pc][s];
|
||||
}
|
||||
|
||||
if (sideToMove == BLACK)
|
||||
si->key ^= Zobrist::side;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Position::set() is an overload to initialize the position object with
|
||||
/// the given endgame code string like "KBPKN". It is mainly a helper to
|
||||
/// get the material key out of an endgame code.
|
||||
|
||||
Position &Position::set(const string &code, Color c, StateInfo *si)
|
||||
{
|
||||
// TODO
|
||||
#if 0
|
||||
assert(code[0] == 'K');
|
||||
|
||||
string sides[] = { code.substr(code.find('K', 1)), // Weak
|
||||
code.substr(0, std::min(code.find('v'), code.find('K', 1))) }; // Strong
|
||||
|
||||
assert(sides[0].length() > 0 && sides[0].length() < 8);
|
||||
assert(sides[1].length() > 0 && sides[1].length() < 8);
|
||||
|
||||
std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
|
||||
|
||||
string fenStr = "8/" + sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/"
|
||||
+ sides[1] + char(8 - sides[1].length() + '0') + "/8 w - - 0 10";
|
||||
|
||||
return set(fenStr, si, nullptr);
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Position::fen() returns a FEN representation of the position. In case of
|
||||
/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
|
||||
|
||||
const string Position::fen() const
|
||||
{
|
||||
// TODO
|
||||
#if 0
|
||||
int emptyCnt;
|
||||
std::ostringstream ss;
|
||||
|
||||
for (Rank r = RANK_8; r >= RANK_1; --r) {
|
||||
for (File f = FILE_A; f <= FILE_C; ++f) {
|
||||
for (emptyCnt = 0; f <= FILE_C && empty(make_square(f, r)); ++f)
|
||||
++emptyCnt;
|
||||
|
||||
if (emptyCnt)
|
||||
ss << emptyCnt;
|
||||
|
||||
if (f <= FILE_C)
|
||||
ss << PieceToChar[piece_on(make_square(f, r))];
|
||||
}
|
||||
|
||||
if (r > RANK_1)
|
||||
ss << '/';
|
||||
}
|
||||
|
||||
ss << (sideToMove == WHITE ? " w " : " b ");
|
||||
|
||||
ss << (" - ")
|
||||
<< st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2;
|
||||
|
||||
return ss.str();
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
int Position::pieces_on_board_count()
|
||||
{
|
||||
nPiecesOnBoard[BLACK] = nPiecesOnBoard[WHITE] = 0;
|
||||
|
@ -1001,7 +1178,7 @@ Key Position::next_primary_key(Move m)
|
|||
|
||||
|
||||
#include "movegen.h"
|
||||
#include "prefetch.h"
|
||||
#include "misc.h"
|
||||
|
||||
const int Position::onBoard[SQUARE_NB] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
@ -1807,6 +1984,12 @@ void Position::rotate(int degrees, int32_t move_, Square square, bool cmdChange
|
|||
}
|
||||
}
|
||||
|
||||
void Position::flip()
|
||||
{
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
|
||||
void Position::print_board()
|
||||
{
|
||||
if (rule.nTotalPiecesEachSide == 12) {
|
||||
|
|
|
@ -62,6 +62,12 @@ struct StateInfo
|
|||
/// elements are not invalidated upon list resizing.
|
||||
typedef std::unique_ptr<std::deque<StateInfo>> StateListPtr;
|
||||
|
||||
/// Position class stores information regarding the board representation as
|
||||
/// pieces, side to move, hash keys, castling info, etc. Important methods are
|
||||
/// do_move() and undo_move(), used by the search to update node info when
|
||||
/// traversing the search tree.
|
||||
class Thread;
|
||||
|
||||
class Position
|
||||
{
|
||||
public:
|
||||
|
@ -71,6 +77,11 @@ public:
|
|||
Position(const Position &) = delete;
|
||||
Position &operator=(const Position &) = delete;
|
||||
|
||||
// FEN string input/output
|
||||
Position &set(const std::string &fenStr, StateInfo *si, Thread *th);
|
||||
Position &set(const std::string &code, Color c, StateInfo *si);
|
||||
const std::string fen() const;
|
||||
|
||||
// Position representation
|
||||
Color color_on(Square s);
|
||||
int getPiecesInHandCount(Color c) const;
|
||||
|
@ -135,6 +146,7 @@ public:
|
|||
void mirror(int32_t move_, Square square, bool cmdChange = true);
|
||||
void turn(int32_t move_, Square square, bool cmdChange = true);
|
||||
void rotate(int degrees, int32_t move_, Square square, bool cmdChange = true);
|
||||
void flip();
|
||||
|
||||
void create_mill_table();
|
||||
int add_mills(Square square);
|
||||
|
@ -159,6 +171,8 @@ public:
|
|||
static bool is_star_square(Square square);
|
||||
|
||||
// private:
|
||||
// Initialization helpers (used while setting up a position)
|
||||
void set_state(StateInfo *si) const;
|
||||
|
||||
// Data members
|
||||
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
Sanmill, a mill game playing engine derived from NineChess 1.5
|
||||
Copyright (C) 2015-2018 liuweilhy (NineChess author)
|
||||
Copyright (C) 2019-2020 Calcitem <calcitem@outlook.com>
|
||||
|
||||
Sanmill 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.
|
||||
|
||||
Sanmill 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 PREFETCH_H
|
||||
#define PREFETCH_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/*
|
||||
prefetch(x) attempts to pre-emptively get the memory pointed to
|
||||
by address "x" into the CPU L1 cache.
|
||||
prefetch(x) should not cause any kind of exception, prefetch(0) is
|
||||
specifically ok.
|
||||
|
||||
prefetch() should be defined by the architecture, if not, the
|
||||
#define below provides a no-op define.
|
||||
|
||||
There are 2 prefetch() macros:
|
||||
|
||||
prefetch(x) - prefetches the cacheline at "x" for read
|
||||
prefetchw(x) - prefetches the cacheline at "x" for write
|
||||
|
||||
there is also PREFETCH_STRIDE which is the architecure-preferred
|
||||
"lookahead" size for prefetching streamed operations.
|
||||
|
||||
*/
|
||||
|
||||
/* L1 cache line size */
|
||||
#define L1_CACHE_SHIFT 7
|
||||
#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT)
|
||||
|
||||
static inline void prefetch(void *addr)
|
||||
{
|
||||
#if defined(__INTEL_COMPILER)
|
||||
// This hack prevents prefetches from being optimized away by
|
||||
// Intel compiler. Both MSVC and gcc seem not be affected by this.
|
||||
__asm__("");
|
||||
#endif
|
||||
|
||||
#if defined(__INTEL_COMPILER) || defined(_MSC_VER)
|
||||
_mm_prefetch((char *)addr, _MM_HINT_T0);
|
||||
#else
|
||||
__builtin_prefetch(addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
#define prefetch(x) __builtin_prefetch(x)
|
||||
|
||||
#define prefetchw(x) __builtin_prefetch(x,1)
|
||||
#endif
|
||||
|
||||
#ifndef PREFETCH_STRIDE
|
||||
#define PREFETCH_STRIDE (4 * L1_CACHE_BYTES)
|
||||
#endif
|
||||
|
||||
static inline void prefetch_range(void *addr, size_t len)
|
||||
{
|
||||
char *cp;
|
||||
char *end = (char *)addr + len;
|
||||
|
||||
for (cp = (char *)addr; cp < end; cp += PREFETCH_STRIDE)
|
||||
prefetch(cp);
|
||||
}
|
||||
|
||||
#endif // PREFETCH_H
|
|
@ -34,6 +34,11 @@
|
|||
|
||||
using namespace CTSL;
|
||||
|
||||
namespace Search
|
||||
{
|
||||
LimitsType Limits;
|
||||
}
|
||||
|
||||
vector<Key> moveHistory;
|
||||
|
||||
AIAlgorithm::AIAlgorithm()
|
||||
|
@ -714,4 +719,23 @@ void AIAlgorithm::loadEndgameFileToHashMap()
|
|||
const QString filename = "endgame.txt";
|
||||
endgameHashMap.load(filename);
|
||||
}
|
||||
|
||||
#endif // ENDGAME_LEARNING
|
||||
|
||||
|
||||
/// Search::init() is called at startup to initialize various lookup tables
|
||||
|
||||
void Search::init()
|
||||
{
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/// Search::clear() resets search state to its initial value
|
||||
|
||||
void Search::clear()
|
||||
{
|
||||
// TODO
|
||||
return;
|
||||
}
|
31
src/search.h
31
src/search.h
|
@ -45,6 +45,37 @@ class MovePicker;
|
|||
using namespace std;
|
||||
using namespace CTSL;
|
||||
|
||||
namespace Search
|
||||
{
|
||||
/// LimitsType struct stores information sent by GUI about available time to
|
||||
/// search the current move, maximum depth/time, or if we are in analysis mode.
|
||||
|
||||
struct LimitsType
|
||||
{
|
||||
LimitsType()
|
||||
{ // Init explicitly due to broken value-initialization of non POD in MSVC
|
||||
time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0);
|
||||
movestogo = depth = mate = perft = infinite = 0;
|
||||
nodes = 0;
|
||||
}
|
||||
|
||||
bool use_time_management() const
|
||||
{
|
||||
return !(mate | movetime | depth | nodes | perft | infinite);
|
||||
}
|
||||
|
||||
std::vector<Move> searchmoves;
|
||||
TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime;
|
||||
int movestogo, depth, mate, perft, infinite;
|
||||
int64_t nodes;
|
||||
};
|
||||
|
||||
extern LimitsType Limits;
|
||||
|
||||
void init();
|
||||
void clear();
|
||||
}
|
||||
|
||||
class AIAlgorithm
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
class Position;
|
||||
|
||||
/// Thread class keeps together all the thread-related stuff. We use
|
||||
/// per-thread pawn and material key tables so that once we get a
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
Fishmill, a UCI Mill Game playing engine derived from Stockfish
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2020 Calcitem <calcitem@outlook.com>
|
||||
|
||||
Fishmill 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.
|
||||
|
||||
Fishmill 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"
|
||||
|
||||
TimeManagement Time; // Our global time management object
|
||||
|
||||
namespace {
|
||||
|
||||
enum TimeType { OptimumTime, MaxTime };
|
||||
|
||||
constexpr int MoveHorizon = 50; // Plan time management at most this many moves ahead
|
||||
constexpr double MaxRatio = 7.3; // When in trouble, we can step over reserved time with this ratio
|
||||
constexpr double StealRatio = 0.34; // 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 the CCRL game database with some simple filtering criteria.
|
||||
|
||||
double move_importance(int ply) {
|
||||
|
||||
constexpr double XScale = 6.85;
|
||||
constexpr double XShift = 64.5;
|
||||
constexpr double Skew = 0.171;
|
||||
|
||||
return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero
|
||||
}
|
||||
|
||||
template<TimeType T>
|
||||
TimePoint remaining(TimePoint myTime, int movesToGo, int ply, TimePoint slowMover) {
|
||||
|
||||
constexpr double TMaxRatio = (T == OptimumTime ? 1.0 : MaxRatio);
|
||||
constexpr double TStealRatio = (T == OptimumTime ? 0.0 : StealRatio);
|
||||
|
||||
double moveImportance = (move_importance(ply) * slowMover) / 100.0;
|
||||
double otherMovesImportance = 0.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 TimePoint(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for 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 TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
|
||||
|
||||
TimePoint minThinkingTime = (TimePoint)Options["Minimum Thinking Time"];
|
||||
TimePoint moveOverhead = (TimePoint)Options["Move Overhead"];
|
||||
TimePoint slowMover = (TimePoint)Options["Slow Mover"];
|
||||
TimePoint npmsec = (TimePoint)Options["nodestime"];
|
||||
TimePoint hypMyTime;
|
||||
|
||||
// If we have to play in 'nodes as time' mode, then convert from time
|
||||
// to nodes, and use resulting values in time management formulas.
|
||||
// WARNING: to avoid time losses, the given npmsec (nodes per millisecond)
|
||||
// must be much lower than the real engine speed.
|
||||
if (npmsec)
|
||||
{
|
||||
if (!availableNodes) // Only once at game start
|
||||
availableNodes = npmsec * limits.time[us]; // Time is in msec
|
||||
|
||||
// Convert from milliseconds to nodes
|
||||
limits.time[us] = TimePoint(availableNodes);
|
||||
limits.inc[us] *= npmsec;
|
||||
limits.npmsec = npmsec;
|
||||
}
|
||||
|
||||
startTime = limits.startTime;
|
||||
optimumTime = maximumTime = 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
|
||||
hypMyTime = limits.time[us]
|
||||
+ limits.inc[us] * (hypMTG - 1)
|
||||
- moveOverhead * (2 + std::min(hypMTG, 40));
|
||||
|
||||
hypMyTime = std::max(hypMyTime, TimePoint(0));
|
||||
|
||||
TimePoint t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, ply, slowMover);
|
||||
TimePoint t2 = minThinkingTime + remaining<MaxTime >(hypMyTime, hypMTG, ply, slowMover);
|
||||
|
||||
optimumTime = std::min(t1, optimumTime);
|
||||
maximumTime = std::min(t2, maximumTime);
|
||||
}
|
||||
|
||||
if (Options["Ponder"])
|
||||
optimumTime += optimumTime / 4;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Fishmill, a UCI Mill Game playing engine derived from Stockfish
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2020 Calcitem <calcitem@outlook.com>
|
||||
|
||||
Fishmill 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.
|
||||
|
||||
Fishmill 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
|
||||
|
||||
#include "misc.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
|
||||
/// The TimeManagement class computes the optimal time to think depending on
|
||||
/// the maximum available time, the game move number and other parameters.
|
||||
|
||||
class TimeManagement {
|
||||
public:
|
||||
void init(Search::LimitsType& limits, Color us, int ply);
|
||||
TimePoint optimum() const { return optimumTime; }
|
||||
TimePoint maximum() const { return maximumTime; }
|
||||
TimePoint elapsed() const { return Search::Limits.npmsec ?
|
||||
TimePoint(Threads.nodes_searched()) : now() - startTime; }
|
||||
|
||||
int64_t availableNodes; // When in 'nodes as time' mode
|
||||
|
||||
private:
|
||||
TimePoint startTime;
|
||||
TimePoint optimumTime;
|
||||
TimePoint maximumTime;
|
||||
};
|
||||
|
||||
extern TimeManagement Time;
|
||||
|
||||
#endif // #ifndef TIMEMAN_H_INCLUDED
|
176
src/types.h
176
src/types.h
|
@ -22,12 +22,90 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
/// When compiling with provided Makefile (e.g. for Linux and OSX), configuration
|
||||
/// is done automatically. To get started type 'make help'.
|
||||
///
|
||||
/// When Makefile is not used (e.g. with Microsoft Visual Studio) some switches
|
||||
/// need to be set manually:
|
||||
///
|
||||
/// -DNDEBUG | Disable debugging mode. Always use this for release.
|
||||
///
|
||||
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to
|
||||
/// | run on some very old machines.
|
||||
///
|
||||
/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
|
||||
/// | only in 64-bit mode and requires hardware with popcnt support.
|
||||
///
|
||||
/// -DUSE_PEXT | Add runtime support for use of pext asm-instruction. Works
|
||||
/// | only in 64-bit mode and requires hardware with pext support.
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Disable some silly and noisy warning from MSVC compiler
|
||||
#pragma warning(disable: 4127) // Conditional expression is constant
|
||||
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
|
||||
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
|
||||
#endif
|
||||
|
||||
/// Predefined macros hell:
|
||||
///
|
||||
/// __GNUC__ Compiler is gcc, Clang or Intel on Linux
|
||||
/// __INTEL_COMPILER Compiler is Intel
|
||||
/// _MSC_VER Compiler is MSVC or Intel on Windows
|
||||
/// _WIN32 Building on Windows (any)
|
||||
/// _WIN64 Building on Windows 64 bit
|
||||
|
||||
#if defined(_WIN64) && defined(_MSC_VER) // No Makefile used
|
||||
# include <intrin.h> // Microsoft header for _BitScanForward64()
|
||||
# define IS_64BIT
|
||||
#endif
|
||||
|
||||
#if defined(USE_POPCNT) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
|
||||
# include <nmmintrin.h> // Intel and Microsoft header for _mm_popcnt_u64()
|
||||
#endif
|
||||
|
||||
#if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
|
||||
# include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch()
|
||||
#endif
|
||||
|
||||
#if defined(USE_PEXT)
|
||||
# include <immintrin.h> // Header for _pext_u64() intrinsic
|
||||
# define pext(b, m) _pext_u64(b, m)
|
||||
#else
|
||||
# define pext(b, m) 0
|
||||
#endif
|
||||
|
||||
#ifdef USE_POPCNT
|
||||
constexpr bool HasPopCnt = true;
|
||||
#else
|
||||
constexpr bool HasPopCnt = false;
|
||||
#endif
|
||||
|
||||
#ifdef USE_PEXT
|
||||
constexpr bool HasPext = true;
|
||||
#else
|
||||
constexpr bool HasPext = false;
|
||||
#endif
|
||||
|
||||
#ifdef IS_64BIT
|
||||
constexpr bool Is64Bit = true;
|
||||
#else
|
||||
constexpr bool Is64Bit = false;
|
||||
#endif
|
||||
|
||||
using Step = uint16_t;
|
||||
using Score = uint32_t;
|
||||
//using Bitboard = uint32_t;
|
||||
typedef uint32_t Bitboard;
|
||||
|
||||
constexpr int MAX_MOVES = 64;
|
||||
constexpr int MAX_PLY = 48;
|
||||
|
||||
#ifdef TRANSPOSITION_TABLE_CUTDOWN
|
||||
using Key = uint32_t;
|
||||
|
@ -38,7 +116,7 @@ using Key = uint64_t;
|
|||
enum Move : int32_t
|
||||
{
|
||||
MOVE_NONE,
|
||||
//MOVE_NULL = 65
|
||||
MOVE_NULL = 65
|
||||
};
|
||||
|
||||
enum MoveType
|
||||
|
@ -97,8 +175,10 @@ enum Value : int8_t
|
|||
VALUE_MATE = 80,
|
||||
VALUE_INFINITE = 125,
|
||||
VALUE_UNKNOWN = std::numeric_limits<int8_t>::min(),
|
||||
VALUE_NONE = VALUE_UNKNOWN,
|
||||
|
||||
VALUE_EACH_PIECE = 5,
|
||||
StoneValue = 5,
|
||||
VALUE_EACH_PIECE = StoneValue,
|
||||
VALUE_EACH_PIECE_INHAND = VALUE_EACH_PIECE,
|
||||
VALUE_EACH_PIECE_ONBOARD = VALUE_EACH_PIECE,
|
||||
VALUE_EACH_PIECE_PLACING_NEEDREMOVE = VALUE_EACH_PIECE,
|
||||
|
@ -108,7 +188,10 @@ enum Value : int8_t
|
|||
VALUE_PVS_WINDOW = VALUE_EACH_PIECE,
|
||||
|
||||
VALUE_PLACING_WINDOW = VALUE_EACH_PIECE_PLACING_NEEDREMOVE + (VALUE_EACH_PIECE_ONBOARD - VALUE_EACH_PIECE_INHAND) + 1,
|
||||
VALUE_MOVING_WINDOW = VALUE_EACH_PIECE_MOVING_NEEDREMOVE + 1
|
||||
VALUE_MOVING_WINDOW = VALUE_EACH_PIECE_MOVING_NEEDREMOVE + 1,
|
||||
|
||||
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
|
||||
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY,
|
||||
};
|
||||
|
||||
enum Rating : int8_t
|
||||
|
@ -237,6 +320,46 @@ enum Rank : int
|
|||
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB = 8
|
||||
};
|
||||
|
||||
|
||||
#if 0
|
||||
/// Score enum stores a middlegame and an endgame value in a single integer (enum).
|
||||
/// The least significant 16 bits are used to store the middlegame value and the
|
||||
/// upper 16 bits are used to store the endgame value. We have to take care to
|
||||
/// avoid left-shifting a signed int to avoid undefined behavior.
|
||||
enum Score : int
|
||||
{
|
||||
SCORE_ZERO
|
||||
};
|
||||
#endif
|
||||
|
||||
// TODO Begin
|
||||
constexpr Score make_score(int mg, int eg)
|
||||
{
|
||||
return Score((int)((unsigned int)eg << 16) + mg);
|
||||
}
|
||||
|
||||
/// Extracting the signed lower and upper 16 bits is not so trivial because
|
||||
/// according to the standard a simple cast to short is implementation defined
|
||||
/// and so is a right shift of a signed integer.
|
||||
inline Value eg_value(Score s)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint16_t u; int16_t s;
|
||||
} eg = { uint16_t(unsigned(s + 0x8000) >> 16) };
|
||||
return Value(eg.s);
|
||||
}
|
||||
|
||||
inline Value mg_value(Score s)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint16_t u; int16_t s;
|
||||
} mg = { uint16_t(unsigned(s)) };
|
||||
return Value(mg.s);
|
||||
}
|
||||
// TODO End
|
||||
|
||||
#define ENABLE_BASE_OPERATORS_ON(T) \
|
||||
constexpr T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \
|
||||
constexpr T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \
|
||||
|
@ -302,6 +425,53 @@ constexpr Square make_square(File file, Rank rank)
|
|||
return Square((file << 3) + rank - 1);
|
||||
}
|
||||
|
||||
constexpr Piece make_piece(Color c, PieceType pt)
|
||||
{
|
||||
if (pt == BLACK_STONE || pt == WHITE_STONE) {
|
||||
return Piece((c << 4));
|
||||
}
|
||||
|
||||
if (pt == BAN) {
|
||||
return BAN_STONE;
|
||||
}
|
||||
|
||||
return NO_PIECE;
|
||||
}
|
||||
|
||||
constexpr PieceType type_of(Piece pc)
|
||||
{
|
||||
if (pc & 0x30) {
|
||||
//return STONE; // TODO
|
||||
}
|
||||
|
||||
if (pc == BAN_STONE) {
|
||||
return BAN;
|
||||
}
|
||||
|
||||
return NO_PIECE_TYPE;
|
||||
}
|
||||
|
||||
inline Color color_of(Piece pc)
|
||||
{
|
||||
assert(pc != NO_PIECE);
|
||||
return Color(pc >> 4);
|
||||
}
|
||||
|
||||
constexpr bool is_ok(Square s)
|
||||
{
|
||||
return s >= SQ_A1 && s <= SQ_C8;
|
||||
}
|
||||
|
||||
constexpr File file_of(Square s)
|
||||
{
|
||||
return File((s >> 3) - 1);
|
||||
}
|
||||
|
||||
constexpr Rank rank_of(Square s)
|
||||
{
|
||||
return Rank(s & 7);
|
||||
}
|
||||
|
||||
#if 0
|
||||
constexpr ring_t ring_of(Square s)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
Fishmill, a UCI Mill Game playing engine derived from Stockfish
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2020 Calcitem <calcitem@outlook.com>
|
||||
|
||||
Fishmill 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.
|
||||
|
||||
Fishmill 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 <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "evaluate.h"
|
||||
#include "movegen.h"
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "timeman.h"
|
||||
#include "tt.h"
|
||||
#include "uci.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
extern vector<string> setup_bench(Position*, istream&);
|
||||
|
||||
namespace {
|
||||
|
||||
// FEN string of the initial position, normal mill game
|
||||
const char *StartFEN = "oooooooo/oooooooo/oooooooo b p 0 1"; // Chess: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
|
||||
// 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, StateListPtr& states) {
|
||||
|
||||
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;
|
||||
|
||||
states = StateListPtr(new std::deque<StateInfo>(1)); // Drop old and create a new one
|
||||
//pos->set(fen, &states->back(), Threads.main()); // TODO
|
||||
|
||||
// Parse move list (if any)
|
||||
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
|
||||
{
|
||||
states->emplace_back();
|
||||
//pos.do_move(m, states->back()); // TODO
|
||||
pos->do_move(m);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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 += (name.empty() ? "" : " ") + token;
|
||||
|
||||
// Read option value (can contain spaces)
|
||||
while (is >> token)
|
||||
value += (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(Position& pos, istringstream& is, StateListPtr& states) {
|
||||
|
||||
Search::LimitsType limits;
|
||||
string token;
|
||||
bool ponderMode = false;
|
||||
|
||||
limits.startTime = now(); // As early as possible!
|
||||
|
||||
while (is >> token)
|
||||
if (token == "searchmoves") // Needs to be the last command on the line
|
||||
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 == "perft") is >> limits.perft;
|
||||
else if (token == "infinite") limits.infinite = 1;
|
||||
else if (token == "ponder") ponderMode = true;
|
||||
|
||||
//Threads.start_thinking(pos, states, limits, ponderMode); // TODO
|
||||
}
|
||||
|
||||
|
||||
// bench() is called when engine receives the "bench" command. Firstly
|
||||
// a list of UCI commands is setup according to bench parameters, then
|
||||
// it is run one by one printing a summary at the end.
|
||||
|
||||
void bench(Position& pos, istream& args, StateListPtr& states) {
|
||||
|
||||
string token;
|
||||
uint64_t num, nodes = 0, cnt = 1;
|
||||
|
||||
vector<string> list = setup_bench(&pos, args);
|
||||
num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0 || s.find("eval") == 0; });
|
||||
|
||||
TimePoint elapsed = now();
|
||||
|
||||
for (const auto& cmd : list)
|
||||
{
|
||||
istringstream is(cmd);
|
||||
is >> skipws >> token;
|
||||
|
||||
if (token == "go" || token == "eval")
|
||||
{
|
||||
cerr << "\nPosition: " << cnt++ << '/' << num << endl;
|
||||
if (token == "go")
|
||||
{
|
||||
go(pos, is, states);
|
||||
Threads.main()->wait_for_search_finished();
|
||||
nodes += Threads.nodes_searched();
|
||||
}
|
||||
else
|
||||
sync_cout << "\n" << Eval::trace(&pos) << sync_endl;
|
||||
}
|
||||
else if (token == "setoption") setoption(is);
|
||||
else if (token == "position") position(&pos, is, states);
|
||||
else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take some while
|
||||
}
|
||||
|
||||
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
|
||||
|
||||
dbg_print(); // Just before exiting
|
||||
|
||||
cerr << "\n==========================="
|
||||
<< "\nTotal time (ms) : " << elapsed
|
||||
<< "\nNodes searched : " << nodes
|
||||
<< "\nNodes/second : " << 1000 * nodes / elapsed << endl;
|
||||
}
|
||||
|
||||
} // 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;
|
||||
string token, cmd;
|
||||
StateListPtr states(new std::deque<StateInfo>(1));
|
||||
|
||||
//pos.set(StartFEN, &states->back(), Threads.main()); // TODO
|
||||
|
||||
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(); // Avoid a stale if getline() returns empty or blank line
|
||||
is >> skipws >> token;
|
||||
|
||||
if ( token == "quit"
|
||||
|| token == "stop")
|
||||
Threads.stop = true;
|
||||
|
||||
// The GUI sends 'ponderhit' to tell us the user has played the expected move.
|
||||
// So 'ponderhit' will be sent if we were told to ponder on the same move the
|
||||
// user has played. We should continue searching but switch from pondering to
|
||||
// normal search.
|
||||
else if (token == "ponderhit")
|
||||
Threads.main()->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 == "setoption") setoption(is);
|
||||
else if (token == "go") go(pos, is, states);
|
||||
else if (token == "position") position(&pos, is, states);
|
||||
else if (token == "ucinewgame") Search::clear();
|
||||
else if (token == "isready") sync_cout << "readyok" << sync_endl;
|
||||
|
||||
// Additional custom non-UCI commands, mainly for debugging.
|
||||
// Do not use these commands during a search!
|
||||
else if (token == "flip") pos.flip();
|
||||
else if (token == "bench") bench(pos, is, states);
|
||||
else if (token == "d") sync_cout << &pos << sync_endl;
|
||||
else if (token == "eval") sync_cout << Eval::trace(&pos) << sync_endl;
|
||||
else if (token == "compiler") sync_cout << compiler_info() << sync_endl;
|
||||
else
|
||||
sync_cout << "Unknown command: " << cmd << sync_endl;
|
||||
|
||||
} while (token != "quit" && argc == 1); // Command line args are one-shot
|
||||
}
|
||||
|
||||
|
||||
/// 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 stones.
|
||||
/// mate <y> Mate in y moves, not plies. If the engine is getting mated
|
||||
/// use negative values for y.
|
||||
|
||||
string UCI::value(Value v) {
|
||||
|
||||
assert(-VALUE_INFINITE < v && v < VALUE_INFINITE);
|
||||
|
||||
stringstream ss;
|
||||
|
||||
if (abs(v) < VALUE_MATE_IN_MAX_PLY)
|
||||
ss << "cp " << v / StoneValue;
|
||||
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 (c1, a7, etc.)
|
||||
|
||||
std::string UCI::square(Square s) {
|
||||
return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) };
|
||||
}
|
||||
|
||||
|
||||
/// 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) {
|
||||
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
|
||||
if (m == MOVE_NONE)
|
||||
return "(none)";
|
||||
|
||||
if (m == MOVE_NULL)
|
||||
return "0000";
|
||||
|
||||
string move = UCI::square(from) + UCI::square(to);
|
||||
|
||||
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(Position* pos, string& str) {
|
||||
|
||||
if (str.length() == 5) // Junior could send promotion piece in uppercase
|
||||
str[4] = char(tolower(str[4]));
|
||||
|
||||
for (const auto& m : MoveList(pos))
|
||||
if (str == UCI::move(m))
|
||||
return m;
|
||||
|
||||
return MOVE_NONE;
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
Fishmill, a UCI Mill Game playing engine derived from Stockfish
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2020 Calcitem <calcitem@outlook.com>
|
||||
|
||||
Fishmill 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.
|
||||
|
||||
Fishmill 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 UCI_H_INCLUDED
|
||||
#define UCI_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 = nullptr);
|
||||
Option(bool v, OnChange = nullptr);
|
||||
Option(const char* v, OnChange = nullptr);
|
||||
Option(double v, int minv, int maxv, OnChange = nullptr);
|
||||
Option(const char* v, const char* cur, OnChange = nullptr);
|
||||
|
||||
Option& operator=(const std::string&);
|
||||
void operator<<(const Option&);
|
||||
operator double() const;
|
||||
operator std::string() const;
|
||||
bool operator==(const char*) 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);
|
||||
std::string pv(Position* pos, Depth depth, Value alpha, Value beta);
|
||||
Move to_move(Position* pos, std::string& str);
|
||||
|
||||
} // namespace UCI
|
||||
|
||||
extern UCI::OptionsMap Options;
|
||||
|
||||
#endif // #ifndef UCI_H_INCLUDED
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
Fishmill, a UCI Mill Game playing engine derived from Stockfish
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad (Stockfish author)
|
||||
Copyright (C) 2020 Calcitem <calcitem@outlook.com>
|
||||
|
||||
Fishmill 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.
|
||||
|
||||
Fishmill 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 <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "misc.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "tt.h"
|
||||
#include "uci.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&) { Search::clear(); }
|
||||
void on_hash_size(const Option& o) { TT.resize((size_t)o); }
|
||||
void on_logger(const Option& o) { start_logger(o); }
|
||||
void on_threads(const Option& o) { Threads.set((size_t)o); }
|
||||
#ifdef TBPROBE
|
||||
void on_tb_path(const Option& o) { Tablebases::init(o); }
|
||||
#endif
|
||||
|
||||
|
||||
/// Our case insensitive less() function as required by UCI protocol
|
||||
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
|
||||
|
||||
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(),
|
||||
[](char c1, char c2) { return tolower(c1) < tolower(c2); });
|
||||
}
|
||||
|
||||
|
||||
/// init() initializes the UCI options to their hard-coded default values
|
||||
|
||||
void init(OptionsMap& o) {
|
||||
|
||||
// at most 2^32 clusters.
|
||||
constexpr int MaxHashMB = Is64Bit ? 131072 : 2048;
|
||||
|
||||
o["Debug Log File"] << Option("", on_logger);
|
||||
o["Contempt"] << Option(24, -100, 100);
|
||||
o["Analysis Contempt"] << Option("Both var Off var White var Black var Both", "Both");
|
||||
o["Threads"] << Option(1, 1, 512, on_threads);
|
||||
o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size);
|
||||
o["Clear Hash"] << Option(on_clear_hash);
|
||||
o["Ponder"] << Option(false);
|
||||
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(84, 10, 1000);
|
||||
o["nodestime"] << Option(0, 0, 10000);
|
||||
o["UCI_AnalyseMode"] << Option(false);
|
||||
o["UCI_LimitStrength"] << Option(false);
|
||||
o["UCI_Elo"] << Option(1350, 1350, 2850);
|
||||
#ifdef TBPROBE
|
||||
o["SyzygyPath"] << Option("<empty>", on_tb_path);
|
||||
o["SyzygyProbeDepth"] << Option(1, 1, 100);
|
||||
o["Syzygy50MoveRule"] << Option(true);
|
||||
o["SyzygyProbeLimit"] << Option(7, 0, 7);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// 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 (const auto& it : om)
|
||||
if (it.second.idx == idx)
|
||||
{
|
||||
const Option& o = it.second;
|
||||
os << "\noption name " << it.first << " type " << o.type;
|
||||
|
||||
if (o.type == "string" || o.type == "check" || o.type == "combo")
|
||||
os << " default " << o.defaultValue;
|
||||
|
||||
if (o.type == "spin")
|
||||
os << " default " << int(stof(o.defaultValue))
|
||||
<< " 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(double v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f)
|
||||
{ defaultValue = currentValue = std::to_string(v); }
|
||||
|
||||
Option::Option(const char* v, const char* cur, OnChange f) : type("combo"), min(0), max(0), on_change(f)
|
||||
{ defaultValue = v; currentValue = cur; }
|
||||
|
||||
Option::operator double() const {
|
||||
assert(type == "check" || type == "spin");
|
||||
return (type == "spin" ? stof(currentValue) : currentValue == "true");
|
||||
}
|
||||
|
||||
Option::operator std::string() const {
|
||||
assert(type == "string");
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
bool Option::operator==(const char* s) const {
|
||||
assert(type == "combo");
|
||||
return !CaseInsensitiveLess()(currentValue, s)
|
||||
&& !CaseInsensitiveLess()(s, 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" && (stof(v) < min || stof(v) > max)))
|
||||
return *this;
|
||||
|
||||
if (type == "combo")
|
||||
{
|
||||
OptionsMap comboMap; // To have case insensitive compare
|
||||
string token;
|
||||
std::istringstream ss(defaultValue);
|
||||
while (ss >> token)
|
||||
comboMap[token] << Option();
|
||||
if (!comboMap.count(v) || v == "var")
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (type != "button")
|
||||
currentValue = v;
|
||||
|
||||
if (on_change)
|
||||
on_change(*this);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace UCI
|
Loading…
Reference in New Issue