Line data Source code
1 : #include "position.hpp"
2 :
3 : #include "bitboardTools.hpp"
4 : #include "dynamicConfig.hpp"
5 : #include "hash.hpp"
6 : #include "logging.hpp"
7 : #include "material.hpp"
8 : #include "moveGen.hpp"
9 : #include "tools.hpp"
10 :
11 450 : template<typename T> T readFromString(const std::string& s) {
12 450 : std::stringstream ss(s);
13 : T tmp;
14 450 : ss >> tmp;
15 450 : return tmp;
16 450 : }
17 :
18 308 : bool readFEN(const std::string& fen, RootPosition& p, bool silent, bool withMoveCount) {
19 308 : if (fen.find_first_not_of(' ') == std::string::npos) {
20 0 : Logging::LogIt(Logging::logError) << "Empty fen";
21 0 : return false;
22 : }
23 :
24 : p.clear(); // "clear" position
25 :
26 308 : std::vector<std::string> strList;
27 308 : std::stringstream iss(fen);
28 616 : std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), back_inserter(strList));
29 :
30 365 : if (!silent) Logging::LogIt(Logging::logInfo) << "Reading fen " << fen;
31 :
32 : // reset position
33 308 : p.h = nullHash;
34 308 : p.ph = nullHash;
35 20020 : for (Square k = 0; k < NbSquare; ++k) p.board(k) = P_none;
36 :
37 : Square j = 1;
38 : Square i = 0;
39 12419 : while ((j <= NbSquare) && (i < (char)strList[0].length())) {
40 12111 : char letter = strList[0].at(i);
41 12111 : ++i;
42 12111 : const Square k = static_cast<Square>((7 - (j - 1) / 8) * 8 + ((j - 1) % 8));
43 12111 : switch (letter) {
44 1313 : case 'p': p.board(k) = P_bp; break;
45 370 : case 'r': p.board(k) = P_br; break;
46 282 : case 'n': p.board(k) = P_bn; break;
47 382 : case 'b': p.board(k) = P_bb; break;
48 144 : case 'q': p.board(k) = P_bq; break;
49 308 : case 'k':
50 308 : p.board(k) = P_bk;
51 308 : p.king[Co_Black] = k;
52 308 : break;
53 1379 : case 'P': p.board(k) = P_wp; break;
54 482 : case 'R': p.board(k) = P_wr; break;
55 462 : case 'N': p.board(k) = P_wn; break;
56 224 : case 'B': p.board(k) = P_wb; break;
57 155 : case 'Q': p.board(k) = P_wq; break;
58 308 : case 'K':
59 308 : p.board(k) = P_wk;
60 308 : p.king[Co_White] = k;
61 308 : break;
62 2156 : case '/': --j; break;
63 : case '1': break;
64 874 : case '2': ++j; break;
65 596 : case '3': j += 2; break;
66 410 : case '4': j += 3; break;
67 184 : case '5': j += 4; break;
68 126 : case '6': j += 5; break;
69 92 : case '7': j += 6; break;
70 649 : case '8': j += 7; break;
71 0 : default: Logging::LogIt(Logging::logError) << "FEN ERROR -1 : invalid character in fen string :" << letter << "\n" << fen; return false;
72 : }
73 12111 : ++j;
74 : }
75 :
76 308 : if (DynamicConfig::isKingMandatory() && (p.king[Co_White] == INVALIDSQUARE || p.king[Co_Black] == INVALIDSQUARE)) {
77 0 : Logging::LogIt(Logging::logError) << "FEN ERROR 0 : missing king";
78 0 : return false;
79 : }
80 :
81 308 : p.c = Co_White; // set the turn; default is white
82 308 : if (strList.size() >= 2) {
83 308 : if (strList[1] == "w") p.c = Co_White;
84 117 : else if (strList[1] == "b")
85 117 : p.c = Co_Black;
86 : else {
87 0 : Logging::LogIt(Logging::logError) << "FEN ERROR 1 : bad Color";
88 0 : return false;
89 : }
90 : }
91 :
92 : // Initialize all castle possibilities (default is none)
93 308 : p.castling = C_none;
94 308 : if (strList.size() >= 3) {
95 : bool found = false;
96 : //Logging::LogIt(Logging::logInfo) << "is not FRC";
97 308 : if (strList[2].find('K') != std::string::npos) {
98 : p.castling |= C_wks;
99 : found = true;
100 : }
101 308 : if (strList[2].find('Q') != std::string::npos) {
102 : p.castling |= C_wqs;
103 : found = true;
104 : }
105 308 : if (strList[2].find('k') != std::string::npos) {
106 : p.castling |= C_bks;
107 : found = true;
108 : }
109 308 : if (strList[2].find('q') != std::string::npos) {
110 : p.castling |= C_bqs;
111 : found = true;
112 : }
113 :
114 237 : if (!found) {
115 378 : for (const char& cr : strList[2]) {
116 192 : if ((cr >= 'A' && cr <= 'H') || (cr >= 'a' && cr <= 'h')) {
117 : //Logging::LogIt(Logging::logInfo) << "Found FRC like castling " << cr;
118 8 : const Color c = std::isupper(cr) ? Co_White : Co_Black;
119 8 : const char kf = static_cast<char>(std::toupper(FileNames[SQFILE(p.king[c])].at(0)));
120 8 : if (std::toupper(cr) > kf) { p.castling |= (c == Co_White ? C_wks : C_bks); }
121 : else {
122 4 : p.castling |= (c == Co_White ? C_wqs : C_bqs);
123 : }
124 8 : if (found && !DynamicConfig::FRC) {
125 0 : Logging::LogIt(Logging::logInfo) << "FRC position found (castling letters), activating FRC";
126 0 : DynamicConfig::FRC = true; // force FRC !
127 : }
128 : found = true;
129 : }
130 : }
131 : }
132 : bool noCastling = false;
133 308 : if (strList[2].find('-') != std::string::npos) {
134 : found = true;
135 : noCastling = true;
136 : /*Logging::LogIt(Logging::logInfo) << "No castling right given" ;*/
137 : }
138 308 : if (!found) {
139 0 : if (!silent) Logging::LogIt(Logging::logWarn) << "No castling right given";
140 : }
141 : else { ///@todo detect illegal stuff in here
142 : bool possibleFRC = false;
143 308 : p.rootInfo().kingInit[Co_White] = p.king[Co_White];
144 308 : p.rootInfo().kingInit[Co_Black] = p.king[Co_Black];
145 308 : if (p.king[Co_White] != Sq_e1 && p.castling & C_w_all) possibleFRC = true;
146 308 : if (p.king[Co_Black] != Sq_e8 && p.castling & C_b_all) possibleFRC = true;
147 308 : if (p.castling & C_wqs) {
148 107 : for (Square s = Sq_a1; s <= Sq_h1; ++s) {
149 107 : if (s < p.king[Co_White] && p.board_const(s) == P_wr) {
150 107 : p.rootInfo().rooksInit[Co_White][CT_OOO] = s;
151 107 : if (s != Sq_a1) possibleFRC = true;
152 : break;
153 : }
154 : }
155 : }
156 308 : if (p.castling & C_wks) {
157 928 : for (Square s = Sq_a1; s <= Sq_h1; ++s) {
158 928 : if (s > p.king[Co_White] && p.board_const(s) == P_wr) {
159 116 : p.rootInfo().rooksInit[Co_White][CT_OO] = s;
160 116 : if (s != Sq_h1) possibleFRC = true;
161 : break;
162 : }
163 : }
164 : }
165 308 : if (p.castling & C_bqs) {
166 73 : for (Square s = Sq_a8; s <= Sq_h8; ++s) {
167 73 : if (s < p.king[Co_Black] && p.board_const(s) == P_br) {
168 73 : p.rootInfo().rooksInit[Co_Black][CT_OOO] = s;
169 73 : if (s != Sq_a8) possibleFRC = true;
170 : break;
171 : }
172 : }
173 : }
174 308 : if (p.castling & C_bks) {
175 648 : for (Square s = Sq_a8; s <= Sq_h8; ++s) {
176 648 : if (s > p.king[Co_Black] && p.board_const(s) == P_br) {
177 81 : p.rootInfo().rooksInit[Co_Black][CT_OO] = s;
178 81 : if (s != Sq_h8) possibleFRC = true;
179 : break;
180 : }
181 : }
182 : }
183 308 : if (possibleFRC && !noCastling && !DynamicConfig::FRC) {
184 0 : Logging::LogIt(Logging::logInfo) << "FRC position found (pieces position), activating FRC";
185 0 : DynamicConfig::FRC = true; // force FRC !
186 : }
187 : }
188 : }
189 0 : else if (!silent)
190 0 : Logging::LogIt(Logging::logInfo) << "No castling right given";
191 :
192 : /*
193 : std::cout << SquareNames[p.rootInfo().kingInit[Co_White]] << std::endl;
194 : std::cout << SquareNames[p.rootInfo().kingInit[Co_Black]] << std::endl;
195 : std::cout << SquareNames[p.rootInfo().rooksInit[Co_White][CT_OOO]] << std::endl;
196 : std::cout << SquareNames[p.rootInfo().rooksInit[Co_White][CT_OO]] << std::endl;
197 : std::cout << SquareNames[p.rootInfo().rooksInit[Co_Black][CT_OOO]] << std::endl;
198 : std::cout << SquareNames[p.rootInfo().rooksInit[Co_Black][CT_OO]] << std::endl;
199 : */
200 :
201 : // read en passant and save it (default is invalid)
202 308 : p.ep = INVALIDSQUARE;
203 308 : if ((strList.size() >= 4) && strList[3] != "-") {
204 2 : if (strList[3].length() >= 2) {
205 2 : if ((strList[3].at(0) >= 'a') && (strList[3].at(0) <= 'h') && ((strList[3].at(1) == '3') || (strList[3].at(1) == '6')))
206 2 : p.ep = stringToSquare(strList[3]);
207 : else {
208 0 : Logging::LogIt(Logging::logError) << "FEN ERROR 2 : bad en passant square : " << strList[3];
209 0 : return false;
210 : }
211 : }
212 : else {
213 0 : Logging::LogIt(Logging::logError) << "FEN ERROR 3 : bad en passant square : " << strList[3];
214 0 : return false;
215 : }
216 : }
217 306 : else if (!silent)
218 57 : Logging::LogIt(Logging::logDebug) << "No en passant square given";
219 :
220 308 : assert(p.ep == INVALIDSQUARE || (SQRANK(p.ep) == 2 || SQRANK(p.ep) == 5));
221 :
222 : // read 50 moves rules
223 308 : if (withMoveCount && strList.size() >= 5) p.fifty = static_cast<decltype(p.fifty)>(readFromString<int>(strList[4]));
224 : else
225 83 : p.fifty = 0;
226 :
227 : // read number of move
228 308 : if (withMoveCount && strList.size() >= 6) p.moves = static_cast<decltype(p.moves)>(readFromString<int>(strList[5]));
229 : else
230 83 : p.moves = 1;
231 :
232 308 : if (p.moves == 0) { // fix a LittleBlitzer bug here ...
233 0 : Logging::LogIt(Logging::logInfo) << "Wrong move counter " << static_cast<int>(p.moves) << " using 1 instead";
234 0 : p.moves = 1;
235 : }
236 :
237 308 : p.halfmoves = static_cast<decltype(p.halfmoves)>((p.moves - 1) * 2 + 1 + (p.c == Co_Black ? 1 : 0));
238 :
239 308 : BBTools::setBitBoards(p);
240 308 : MaterialHash::initMaterial(p);
241 308 : p.h = computeHash(p);
242 308 : p.ph = computePHash(p);
243 :
244 : #ifdef WITH_NNUE
245 : // If position is associated with an NNUE evaluator,
246 : // we reset the evaluator using the new position state.
247 308 : if (DynamicConfig::useNNUE && p.hasEvaluator()) p.resetNNUEEvaluator(p.evaluator());
248 : #endif
249 :
250 308 : p.rootInfo().initCaslingPermHashTable();
251 :
252 308 : return true;
253 308 : }
254 :
255 308 : void RootInformation::initCaslingPermHashTable() {
256 : // works also for FRC !
257 : castlePermHashTable.fill(C_all);
258 308 : if (isValidSquare(kingInit[Co_White])){
259 308 : castlePermHashTable[kingInit[Co_White]] = C_all_but_w;
260 308 : if (isValidSquare(rooksInit[Co_White][CT_OOO])) castlePermHashTable[rooksInit[Co_White][CT_OOO]] = C_all_but_wqs;
261 308 : if (isValidSquare(rooksInit[Co_White][CT_OO] )) castlePermHashTable[rooksInit[Co_White][CT_OO]] = C_all_but_wks;
262 : }
263 308 : if (isValidSquare(kingInit[Co_Black])){
264 308 : castlePermHashTable[kingInit[Co_Black]] = C_all_but_b;
265 308 : if (isValidSquare(rooksInit[Co_Black][CT_OOO])) castlePermHashTable[rooksInit[Co_Black][CT_OOO]] = C_all_but_bqs;
266 308 : if (isValidSquare(rooksInit[Co_Black][CT_OO] )) castlePermHashTable[rooksInit[Co_Black][CT_OO]] = C_all_but_bks;
267 : }
268 308 : }
269 :
270 180142912 : Position::~Position() {
271 : //I do not delete the root pointer, only a RootPosition can do that
272 180142912 : }
273 :
274 160336 : Position::Position() {}
275 :
276 : /*
277 : #include <immintrin.h>
278 :
279 : Position & Position::operator=(const Position & p){
280 : if(&p != this){
281 : constexpr size_t n = 7; // => sizeof(Position) == 224b
282 : static_assert(sizeof(Position) == n*sizeof(__m256i));
283 : __m256i * me_256 = reinterpret_cast<__m256i *>(this);
284 : const __m256i * other_256 = reinterpret_cast<const __m256i *>(&p);
285 : for(size_t i = 0; i < n; ++i, ++me_256, ++other_256){
286 : _mm256_store_si256(me_256, _mm256_load_si256(other_256));
287 : }
288 : }
289 : return *this;
290 : }
291 :
292 : Position::Position(const Position& p) {
293 : if(&p != this){
294 : this->operator=(p);
295 : }
296 : }
297 : */
298 :
299 2 : RootPosition::RootPosition(const std::string& fen, bool withMoveCount) : RootPosition() {
300 2 : readFEN(fen, *this, true, withMoveCount);
301 2 : }
302 :
|