Line data Source code
1 : #include "tools.hpp"
2 :
3 : #include "dynamicConfig.hpp"
4 : #include "evalDef.hpp"
5 : #include "hash.hpp"
6 : #include "logging.hpp"
7 : #include "moveApply.hpp"
8 : #include "pieceTools.hpp"
9 : #include "position.hpp"
10 : #include "positionTools.hpp"
11 : #include "searcher.hpp"
12 : #include "score.hpp"
13 : #include "threading.hpp"
14 :
15 19 : std::string trim(const std::string& str, const std::string& whitespace) {
16 : const auto strBegin = str.find_first_not_of(whitespace);
17 19 : if (strBegin == std::string::npos) return ""; // no content
18 : const auto strEnd = str.find_last_not_of(whitespace);
19 19 : const auto strRange = strEnd - strBegin + 1;
20 19 : return str.substr(strBegin, strRange);
21 : }
22 :
23 1 : void tokenize(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters) {
24 : std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
25 : std::string::size_type pos = str.find_first_of(delimiters, lastPos);
26 3 : while (std::string::npos != pos || std::string::npos != lastPos) {
27 4 : tokens.push_back(str.substr(lastPos, pos - lastPos));
28 : lastPos = str.find_first_not_of(delimiters, pos);
29 : pos = str.find_first_of(delimiters, lastPos);
30 : }
31 1 : }
32 :
33 0 : std::vector<std::string> tokenize(const std::string& str, const std::string& delimiters) {
34 0 : std::vector<std::string> tokens;
35 0 : tokenize(str,tokens,delimiters);
36 0 : return tokens;
37 0 : }
38 :
39 : #ifdef DEBUG_KING_CAP
40 : void debug_king_cap(const Position& p) {
41 : if (!p.whiteKing() || !p.blackKing()) {
42 : std::cout << "no more king" << std::endl;
43 : std::cout << ToString(p) << std::endl;
44 : std::cout << ToString(p.lastMove) << std::endl;
45 : Distributed::finalize();
46 : exit(1);
47 : }
48 : }
49 : #else
50 8064674 : void debug_king_cap(const Position&) { ; }
51 : #endif
52 :
53 0 : std::string ToString(const MiniMove& m) { return ToString(static_cast<Move>(m), false); }
54 :
55 5398 : std::string ToString(const Move& m, bool withScore) {
56 5398 : if (sameMove(m, INVALIDMOVE)) return "invalid move"; ///@todo 0000 ?
57 5382 : if (sameMove(m, NULLMOVE)) return "null move"; ///@todo 0000 ?
58 :
59 5422 : const std::string score = (withScore ? " (" + std::to_string(Move2Score(m)) + ")" : "");
60 : // FRC castling is encoded king takes rook
61 : // standard chess castling is encoded with Smith notation
62 5382 : if (!DynamicConfig::FRC) {
63 10750 : switch (Move2Type(m)) {
64 10 : case T_bks: return "e8g8" + score;
65 18 : case T_wks: return "e1g1" + score;
66 0 : case T_bqs: return "e8c8" + score;
67 0 : case T_wqs: return "e1c1" + score;
68 : default:
69 : break;
70 : }
71 : }
72 : static constexpr array1d<std::string_view,T_max> promSuffixe = {"", "", "", "", "q", "r", "b", "n", "q", "r", "b", "n", "", "", "", ""};
73 10708 : const std::string_view & prom = promSuffixe[Move2Type(m)];
74 : ///@todo remove string CTOR here in C++26 ;-)
75 21416 : return std::string(SquareNames[Move2From(m)]) + std::string(SquareNames[Move2To(m)]) + std::string(prom) + score;
76 : }
77 :
78 475 : std::string ToString(const PVList& moves) {
79 475 : std::stringstream ss;
80 4594 : for (const auto &m : moves) {
81 4119 : if (m == INVALIDMOVE) break;
82 12357 : ss << ToString(m) << " ";
83 : }
84 475 : return ss.str();
85 475 : }
86 :
87 0 : std::string ToString(const Position::Material& mat) {
88 0 : std::stringstream str;
89 : str << "\n"
90 0 : << Logging::_protocolComment[Logging::ct] << "K :" << static_cast<int>(mat[Co_White][M_k]) << "\n"
91 0 : << Logging::_protocolComment[Logging::ct] << "Q :" << static_cast<int>(mat[Co_White][M_q]) << "\n"
92 0 : << Logging::_protocolComment[Logging::ct] << "R :" << static_cast<int>(mat[Co_White][M_r]) << "\n"
93 0 : << Logging::_protocolComment[Logging::ct] << "B :" << static_cast<int>(mat[Co_White][M_b]) << "\n"
94 0 : << Logging::_protocolComment[Logging::ct] << "L :" << static_cast<int>(mat[Co_White][M_bl]) << "\n"
95 0 : << Logging::_protocolComment[Logging::ct] << "D :" << static_cast<int>(mat[Co_White][M_bd]) << "\n"
96 0 : << Logging::_protocolComment[Logging::ct] << "N :" << static_cast<int>(mat[Co_White][M_n]) << "\n"
97 0 : << Logging::_protocolComment[Logging::ct] << "P :" << static_cast<int>(mat[Co_White][M_p]) << "\n"
98 0 : << Logging::_protocolComment[Logging::ct] << "Ma :" << static_cast<int>(mat[Co_White][M_M]) << "\n"
99 0 : << Logging::_protocolComment[Logging::ct] << "Mi :" << static_cast<int>(mat[Co_White][M_m]) << "\n"
100 0 : << Logging::_protocolComment[Logging::ct] << "T :" << static_cast<int>(mat[Co_White][M_t]) << "\n";
101 : str << "\n"
102 0 : << Logging::_protocolComment[Logging::ct] << "k :" << static_cast<int>(mat[Co_Black][M_k]) << "\n"
103 0 : << Logging::_protocolComment[Logging::ct] << "q :" << static_cast<int>(mat[Co_Black][M_q]) << "\n"
104 0 : << Logging::_protocolComment[Logging::ct] << "r :" << static_cast<int>(mat[Co_Black][M_r]) << "\n"
105 0 : << Logging::_protocolComment[Logging::ct] << "b :" << static_cast<int>(mat[Co_Black][M_b]) << "\n"
106 0 : << Logging::_protocolComment[Logging::ct] << "l :" << static_cast<int>(mat[Co_Black][M_bl]) << "\n"
107 0 : << Logging::_protocolComment[Logging::ct] << "d :" << static_cast<int>(mat[Co_Black][M_bd]) << "\n"
108 0 : << Logging::_protocolComment[Logging::ct] << "n :" << static_cast<int>(mat[Co_Black][M_n]) << "\n"
109 0 : << Logging::_protocolComment[Logging::ct] << "p :" << static_cast<int>(mat[Co_Black][M_p]) << "\n"
110 0 : << Logging::_protocolComment[Logging::ct] << "ma :" << static_cast<int>(mat[Co_Black][M_M]) << "\n"
111 0 : << Logging::_protocolComment[Logging::ct] << "mi :" << static_cast<int>(mat[Co_Black][M_m]) << "\n"
112 0 : << Logging::_protocolComment[Logging::ct] << "t :" << static_cast<int>(mat[Co_Black][M_t]) << "\n";
113 0 : return str.str();
114 0 : }
115 :
116 442 : std::string ToString(const Position& p, bool noEval) {
117 442 : std::stringstream ss;
118 : ss << "Position" << std::endl;
119 3978 : for (Square j = 7; j >= 0; --j) {
120 3536 : ss << Logging::_protocolComment[Logging::ct] << " +-+-+-+-+-+-+-+-+" << std::endl;
121 3536 : ss << Logging::_protocolComment[Logging::ct] << " |";
122 31824 : for (Square i = 0; i < 8; ++i) ss << PieceTools::getName/*UTF*/(p, static_cast<Square>(i + j * 8)) << '|';
123 : ss << std::endl;
124 : }
125 442 : ss << Logging::_protocolComment[Logging::ct] << " +-+-+-+-+-+-+-+-+" << std::endl;
126 442 : if (p.ep >= 0) ss << Logging::_protocolComment[Logging::ct] << " ep " << SquareNames[p.ep] << std::endl;
127 : //ss << Logging::_protocolComment[Logging::ct] << " wk " << (p.king[Co_White]!=INVALIDSQUARE?SquareNames[p.king[Co_White]]:"none") << std::endl;
128 : //ss << Logging::_protocolComment[Logging::ct] << " bk " << (p.king[Co_Black]!=INVALIDSQUARE?SquareNames[p.king[Co_Black]]:"none") << std::endl;
129 1094 : ss << Logging::_protocolComment[Logging::ct] << " Turn " << (p.c == Co_White ? "white" : "black") << std::endl;
130 : ScoreType sc = 0;
131 442 : if (!noEval) {
132 442 : EvalData data;
133 : #ifdef WITH_NNUE
134 442 : NNUEEvaluator evaluator;
135 442 : if (!p.associatedEvaluator) {
136 0 : p.associatedEvaluator = &evaluator;
137 0 : p.resetNNUEEvaluator(evaluator);
138 : }
139 : #endif
140 442 : const bool tmp = DynamicConfig::forceNNUE;
141 442 : if (DynamicConfig::useNNUE) DynamicConfig::forceNNUE = true;
142 442 : sc = eval(p, data, ThreadPool::instance().main(), true);
143 442 : if (DynamicConfig::useNNUE) DynamicConfig::forceNNUE = tmp;
144 884 : ss << Logging::_protocolComment[Logging::ct] << " Phase " << data.gp << std::endl;
145 884 : ss << Logging::_protocolComment[Logging::ct] << " Static score " << sc << std::endl;
146 884 : ss << Logging::_protocolComment[Logging::ct] << " Hash " << computeHash(p) << std::endl;
147 : }
148 1326 : ss << Logging::_protocolComment[Logging::ct] << " FEN " << GetFEN(p);
149 : //ss << ToString(p.mat);
150 442 : return ss.str();
151 442 : }
152 :
153 2 : std::string ToString(const BitBoard& b) {
154 2 : std::bitset<64> bs(b);
155 2 : std::stringstream ss;
156 18 : for (int j = 7; j >= 0; --j) {
157 16 : ss << "\n";
158 16 : ss << Logging::_protocolComment[Logging::ct] << "+-+-+-+-+-+-+-+-+"
159 16 : << "\n";
160 16 : ss << Logging::_protocolComment[Logging::ct] << "|";
161 144 : for (int i = 0; i < 8; ++i) ss << (bs[i + j * 8] ? "X" : " ") << '|';
162 : }
163 2 : ss << "\n";
164 2 : ss << Logging::_protocolComment[Logging::ct] << "+-+-+-+-+-+-+-+-+";
165 2 : return ss.str();
166 2 : }
167 :
168 0 : bool checkEval(const Position & p, ScoreType e, Searcher & context, const std::string & txt){
169 0 : EvalData data;
170 0 : const ScoreType f = eval(p, data, context, true, true);
171 : #ifdef DEBUG_EVALSYM
172 : Position p2 = p;
173 : p2.c = ~p2.c;
174 : const ScoreType g = eval(p2, data, context, true, false);
175 : if ( Abs(f + g - 2*EvalConfig::tempo) > 2){
176 : std::cout << "*********************" << std::endl;
177 : std::cout << ToString(p) << std::endl;
178 : std::cout << f << std::endl;
179 : std::cout << g - 2*EvalConfig::tempo << std::endl;
180 : std::cout << "EVALSYMERROR" << std::endl;
181 : }
182 : #endif
183 0 : if ( Abs(e - f) > std::max(Abs(e)/20,10)){
184 : std::cout << "*********************" << std::endl;
185 0 : std::cout << ToString(p) << std::endl;
186 0 : std::cout << e << std::endl;
187 0 : std::cout << f << std::endl;
188 : std::cout << "EVALERROR : " << txt << std::endl;
189 0 : return false;
190 : }
191 : else {
192 : std::cout << "EVALOK : " << txt << std::endl;
193 0 : return true;
194 : }
195 : }
196 :
197 : namespace {
198 0 : uint64_t numberOf(const Position &p, Piece t) { return BB::countBit(p.pieces_const(t)); }
199 : } // namespace
200 :
201 0 : std::string showAlgAbr(const Move m, const Position &p) {
202 0 : const Square from = Move2From(m);
203 0 : const Square to = Move2To(m);
204 : const MType mtype = Move2Type(m);
205 : if (m == INVALIDMOVE) return "xx";
206 :
207 : bool isCheck = false;
208 : bool isNotLegal = false;
209 0 : Position p2 = p;
210 : #ifdef WITH_NNUE
211 0 : NNUEEvaluator evaluator = p.evaluator();
212 : p2.associateEvaluator(evaluator);
213 : #endif
214 0 : if (const MoveInfo moveInfo(p2,m); applyMove(p2, moveInfo)) {
215 0 : if (isPosInCheck(p2)) isCheck = true;
216 : }
217 : else {
218 : isNotLegal = true;
219 : }
220 :
221 0 : if (mtype == T_wks || mtype == T_bks) return std::string("0-0") + (isCheck ? "+" : "") + (isNotLegal ? "~" : "");
222 0 : if (mtype == T_wqs || mtype == T_bqs) return std::string("0-0-0") + (isCheck ? "+" : "") + (isNotLegal ? "~" : "");
223 :
224 : std::string s;
225 0 : Piece t = p.board_const(from);
226 :
227 : // add piece type if not pawn
228 0 : s += PieceNames[PieceIdx(static_cast<Piece>(Abs(t)))];
229 0 : if (t == P_wp || t == P_bp) s.clear(); // no piece symbol for pawn
230 :
231 : // ensure move is not ambiguous
232 : bool isSamePiece = false;
233 : bool isSameFile = false;
234 : bool isSameRank = false;
235 0 : if (numberOf(p, t) > 1) {
236 0 : std::vector<Square> v;
237 0 : BitBoard b = p.pieces_const(t);
238 0 : while (b) v.emplace_back(BB::popBit(b));
239 0 : for (const auto & sq : v) {
240 0 : if (sq == from) continue; // to not compare to myself ...
241 0 : MoveList l;
242 0 : MoveGen::generateSquare<MoveGen::GP_all>(p, l, sq);
243 0 : for (const auto & m2 : l) {
244 0 : if (m2 == m) continue; // to not compare to myself ... should no happend thanks to previous verification
245 0 : Position p3 = p;
246 0 : const MoveInfo moveInfo2(p3, m2);
247 0 : if (applyMove(p3, moveInfo2)) { // only if move is legal
248 0 : if (Move2To(m2) == to &&
249 0 : (t == p.board_const(Move2From(m2)))) { // another move is landing on the same square with the same piece type
250 : isSamePiece = true;
251 0 : if (SQFILE(Move2From(m2)) == SQFILE(from)) { isSameFile = true; }
252 0 : if (SQRANK(Move2From(m2)) == SQRANK(from)) { isSameRank = true; }
253 : }
254 : }
255 0 : }
256 : }
257 0 : }
258 :
259 0 : if ((t == P_wp || t == P_bp) && isCapture(m)) s += FileNames[SQFILE(from)];
260 0 : else if (isSamePiece) {
261 0 : if (!isSameFile) s += FileNames[SQFILE(from)];
262 0 : else if (isSameFile && !isSameRank)
263 0 : s += RankNames[SQRANK(from)];
264 : else if (isSameFile && isSameRank) {
265 0 : s += FileNames[SQFILE(from)];
266 0 : s += RankNames[SQRANK(from)];
267 : }
268 : }
269 :
270 : // add 'x' if capture
271 0 : if (isCapture(m)) { s += "x"; }
272 :
273 : // add landing position
274 0 : s += SquareNames[to];
275 :
276 : // and promotion to
277 0 : if (isPromotion(m)) {
278 0 : switch (mtype) {
279 : case T_cappromq:
280 : case T_promq:
281 : s += "=";
282 : s += "Q";
283 : break;
284 : case T_cappromr:
285 : case T_promr:
286 : s += "=";
287 : s += "R";
288 : break;
289 : case T_cappromb:
290 : case T_promb:
291 : s += "=";
292 : s += "B";
293 : break;
294 : case T_cappromn:
295 : case T_promn:
296 : s += "=";
297 : s += "N";
298 : break;
299 : default: break;
300 : }
301 : }
302 :
303 0 : if (isCheck) s += "+";
304 0 : if (isNotLegal) s += "~";
305 :
306 0 : return s;
307 0 : }
|