Line data Source code
1 : #include "moveApply.hpp"
2 :
3 : #include "attack.hpp"
4 : #include "bitboard.hpp"
5 : #include "bitboardTools.hpp"
6 : #include "hash.hpp"
7 : #include "material.hpp"
8 : #include "movePseudoLegal.hpp"
9 : #include "position.hpp"
10 : #include "tools.hpp"
11 :
12 179492925 : MoveInfo::MoveInfo(const Position& p, const Move _m):
13 179492925 : m(_m),
14 179492925 : from(Move2From(_m)),
15 179492925 : to(correctedMove2ToKingDest(_m)),
16 179492925 : king{p.king[Co_White], p.king[Co_Black]},
17 179492925 : ep(p.ep),
18 179492925 : type(Move2Type(_m)),
19 179492925 : fromP(p.board_const(from)),
20 179492925 : toP(p.board_const(to)), // won't be used for EP
21 179492925 : fromId(PieceIdx(fromP)),
22 179492925 : isCapNoEP(toP != P_none) {
23 179492925 : assert(isValidSquare(from));
24 179492925 : assert(isValidSquare(to));
25 : assert(isValidMoveType(type));
26 179492925 : }
27 :
28 : template<Color c>
29 : FORCE_FINLINE void movePieceCastle(Position& p, const CastlingTypes ct, const Square kingDest, const Square rookDest) {
30 : START_TIMER
31 : constexpr Piece pk = c == Co_White ? P_wk : P_bk;
32 : constexpr Piece pr = c == Co_White ? P_wr : P_br;
33 : constexpr CastlingRights ks = c == Co_White ? C_wks : C_bks;
34 : constexpr CastlingRights qs = c == Co_White ? C_wqs : C_bqs;
35 139703 : BBTools::unSetBit(p, p.king[c]);
36 139703 : BB::_unSetBit(p.allPieces[c], p.king[c]);
37 139703 : BBTools::unSetBit(p, p.rootInfo().rooksInit[c][ct]);
38 139703 : BB::_unSetBit(p.allPieces[c], p.rootInfo().rooksInit[c][ct]);
39 : BBTools::setBit(p, kingDest, pk);
40 : BB::_setBit(p.allPieces[c], kingDest);
41 : BBTools::setBit(p, rookDest, pr);
42 : BB::_setBit(p.allPieces[c], rookDest);
43 139703 : p.board(p.king[c]) = P_none;
44 139703 : p.board(p.rootInfo().rooksInit[c][ct]) = P_none;
45 139703 : p.board(kingDest) = pk;
46 139703 : p.board(rookDest) = pr;
47 139703 : p.h ^= Zobrist::ZT[p.king[c]][pk + PieceShift];
48 139703 : p.ph ^= Zobrist::ZT[p.king[c]][pk + PieceShift];
49 139703 : p.h ^= Zobrist::ZT[p.rootInfo().rooksInit[c][ct]][pr + PieceShift];
50 139703 : p.h ^= Zobrist::ZT[kingDest][pk + PieceShift];
51 139703 : p.ph ^= Zobrist::ZT[kingDest][pk + PieceShift];
52 139703 : p.h ^= Zobrist::ZT[rookDest][pr + PieceShift];
53 139703 : p.king[c] = kingDest;
54 139703 : p.h ^= Zobrist::ZTCastling[p.castling];
55 : p.castling &= ~(ks | qs);
56 139703 : p.h ^= Zobrist::ZTCastling[p.castling];
57 : STOP_AND_SUM_TIMER(MovePiece)
58 139703 : }
59 :
60 :
61 179272992 : void movePiece(Position& p, const Square from, const Square to, const Piece fromP, const Piece toP, const bool isCapture = false, const Piece prom = P_none) {
62 179272992 : assert(isValidSquare(from));
63 179272992 : assert(isValidSquare(to));
64 0 : assert(isValidPieceNotNone(fromP));
65 : START_TIMER
66 : const int fromId = PieceIdx(fromP);
67 : const int toId = PieceIdx(toP);
68 179272992 : const Piece toPnew = prom != P_none ? prom : fromP;
69 : const int toIdnew = PieceIdx(toPnew);
70 : // update board
71 179272992 : p.board(from) = P_none;
72 179272992 : p.board(to) = toPnew;
73 : // update bitboard
74 : BBTools::unSetBit(p, from, fromP);
75 179272992 : BB::_unSetBit(p.allPieces[p.c], from);
76 179272992 : if (toP != P_none) {
77 : BBTools::unSetBit(p, to, toP);
78 10789606 : BB::_unSetBit(p.allPieces[~p.c], to);
79 : }
80 : BBTools::setBit(p, to, toPnew);
81 : BB::_setBit(p.allPieces[p.c], to);
82 : // update Zobrist hash
83 179272992 : p.h ^= Zobrist::ZT[from][fromId]; // remove fromP at from
84 179272992 : p.h ^= Zobrist::ZT[to][toIdnew]; // add fromP (or prom) at to
85 : // piece that have influence on pawn hash
86 : static constexpr array1d<bool,NbPiece> helperPawnHash_ = {true, false, false, false, false, true, false, true, false, false, false, false, true};
87 179272992 : if (helperPawnHash_[fromId]) {
88 94089875 : p.ph ^= Zobrist::ZT[from][fromId]; // remove fromP at from
89 94089875 : if (prom == P_none) p.ph ^= Zobrist::ZT[to][toIdnew]; // add fromP (if not prom) at to
90 : }
91 179272992 : if (isCapture) { // if capture remove toP at to
92 10789606 : p.h ^= Zobrist::ZT[to][toId];
93 10789606 : if (abs(toP) == P_wp || abs(toP) == P_wk) p.ph ^= Zobrist::ZT[to][toId];
94 : }
95 : // consequences on castling
96 328225352 : if (p.castling && (p.rootInfo().castlePermHashTable[from] ^ p.rootInfo().castlePermHashTable[to])) {
97 9919325 : p.h ^= Zobrist::ZTCastling[p.castling];
98 : p.castling &= (p.rootInfo().castlePermHashTable[from] & p.rootInfo().castlePermHashTable[to]);
99 9919325 : p.h ^= Zobrist::ZTCastling[p.castling];
100 : }
101 :
102 : // update king position
103 179272992 : if (fromP == P_wk) p.king[Co_White] = to;
104 176282919 : else if (fromP == P_bk)
105 9044008 : p.king[Co_Black] = to;
106 :
107 : // king capture : this can append in some chess variants
108 179272992 : if (toP == P_wk) p.king[Co_White] = INVALIDSQUARE;
109 179272992 : else if (toP == P_bk) p.king[Co_Black] = INVALIDSQUARE;
110 :
111 : STOP_AND_SUM_TIMER(MovePiece)
112 179272992 : }
113 :
114 491394 : void applyNull(Searcher&, Position& pN) {
115 : START_TIMER
116 491394 : pN.c = ~pN.c;
117 491394 : pN.h ^= Zobrist::ZT[3][13];
118 491394 : pN.h ^= Zobrist::ZT[4][13];
119 491394 : if (pN.ep != INVALIDSQUARE) pN.h ^= Zobrist::ZT[pN.ep][13];
120 491394 : pN.ep = INVALIDSQUARE;
121 491394 : pN.lastMove = NULLMOVE;
122 491394 : if (pN.c == Co_White) ++pN.moves;
123 491394 : ++pN.halfmoves;
124 : STOP_AND_SUM_TIMER(Apply)
125 491394 : }
126 :
127 179492925 : bool applyMove(Position& p, const MoveInfo & moveInfo, [[maybe_unused]] const bool noNNUEUpdate) {
128 0 : assert(isValidMove(moveInfo.m));
129 : START_TIMER
130 : #ifdef DEBUG_MATERIAL
131 : Position previous = p;
132 : #endif
133 : #ifdef DEBUG_APPLY
134 : if (!isPseudoLegal(p, moveInfo.m)) {
135 : Logging::LogIt(Logging::logError) << ToString(moveInfo.m) << " " << moveInfo.type;
136 : Logging::LogIt(Logging::logFatal) << "Apply error, not legal " << ToString(p);
137 : assert(false);
138 : }
139 : #endif
140 :
141 179492925 : switch (moveInfo.type) {
142 : case T_reserved:
143 0 : Logging::LogIt(Logging::logError) << ToString(moveInfo.m) << " " << static_cast<int>(moveInfo.type);
144 0 : Logging::LogIt(Logging::logFatal) << "Apply error, move is not legal (reserved). " << ToString(p);
145 0 : break;
146 178850098 : case T_std:
147 : case T_capture:
148 : // update material
149 178850098 : if (moveInfo.isCapNoEP) { --p.mat[~p.c][Abs(moveInfo.toP)]; }
150 : // update hash, BB, board and castling rights
151 178850098 : movePiece(p, moveInfo.from, moveInfo.to, moveInfo.fromP, moveInfo.toP, moveInfo.type == T_capture);
152 178850098 : break;
153 80230 : case T_ep:
154 : {
155 80230 : assert(p.ep != INVALIDSQUARE);
156 80230 : assert(SQRANK(p.ep) == EPRank[p.c]);
157 80230 : const auto epCapSq = static_cast<Square>(p.ep + (p.c == Co_White ? -8 : +8));
158 80230 : assert(isValidSquare(epCapSq));
159 80230 : BBTools::unSetBit(p, epCapSq, ~moveInfo.fromP); // BEFORE setting p.b new shape !!!
160 80230 : BB::_unSetBit(p.allPieces[~p.c], epCapSq);
161 80230 : BBTools::unSetBit(p, moveInfo.from, moveInfo.fromP);
162 : BB::_unSetBit(p.allPieces[p.c], moveInfo.from);
163 80230 : BBTools::setBit(p, moveInfo.to, moveInfo.fromP);
164 : BB::_setBit(p.allPieces[p.c], moveInfo.to);
165 80230 : p.board(moveInfo.from) = P_none;
166 80230 : p.board(moveInfo.to) = moveInfo.fromP;
167 80230 : p.board(epCapSq) = P_none;
168 :
169 80230 : p.h ^= Zobrist::ZT[moveInfo.from][moveInfo.fromId]; // remove fromP at from
170 80230 : p.h ^= Zobrist::ZT[epCapSq][PieceIdx(p.c == Co_White ? P_bp : P_wp)]; // remove captured pawn
171 80230 : p.h ^= Zobrist::ZT[moveInfo.to][moveInfo.fromId]; // add fromP at to
172 :
173 80230 : p.ph ^= Zobrist::ZT[moveInfo.from][moveInfo.fromId]; // remove fromP at from
174 80230 : p.ph ^= Zobrist::ZT[epCapSq][PieceIdx(p.c == Co_White ? P_bp : P_wp)]; // remove captured pawn
175 80230 : p.ph ^= Zobrist::ZT[moveInfo.to][moveInfo.fromId]; // add fromP at to
176 :
177 80230 : --p.mat[~p.c][M_p];
178 : }
179 80230 : break;
180 107590 : case T_promq:
181 : case T_cappromq:
182 107590 : MaterialHash::updateMaterialProm(p, moveInfo.to, moveInfo.type);
183 128901 : movePiece(p, moveInfo.from, moveInfo.to, moveInfo.fromP, moveInfo.toP, moveInfo.type == T_cappromq, (p.c == Co_White ? P_wq : P_bq));
184 107590 : break;
185 105128 : case T_promr:
186 : case T_cappromr:
187 105128 : MaterialHash::updateMaterialProm(p, moveInfo.to, moveInfo.type);
188 126053 : movePiece(p, moveInfo.from, moveInfo.to, moveInfo.fromP, moveInfo.toP, moveInfo.type == T_cappromr, (p.c == Co_White ? P_wr : P_br));
189 105128 : break;
190 105089 : case T_promb:
191 : case T_cappromb:
192 105089 : MaterialHash::updateMaterialProm(p, moveInfo.to, moveInfo.type);
193 126014 : movePiece(p, moveInfo.from, moveInfo.to, moveInfo.fromP, moveInfo.toP, moveInfo.type == T_cappromb, (p.c == Co_White ? P_wb : P_bb));
194 105089 : break;
195 105087 : case T_promn:
196 : case T_cappromn:
197 105087 : MaterialHash::updateMaterialProm(p, moveInfo.to, moveInfo.type);
198 126012 : movePiece(p, moveInfo.from, moveInfo.to, moveInfo.fromP, moveInfo.toP, moveInfo.type == T_cappromn, (p.c == Co_White ? P_wn : P_bn));
199 105087 : break;
200 : case T_wks: movePieceCastle<Co_White>(p, CT_OO, Sq_g1, Sq_f1); break;
201 : case T_wqs: movePieceCastle<Co_White>(p, CT_OOO, Sq_c1, Sq_d1); break;
202 : case T_bks: movePieceCastle<Co_Black>(p, CT_OO, Sq_g8, Sq_f8); break;
203 : case T_bqs: movePieceCastle<Co_Black>(p, CT_OOO, Sq_c8, Sq_d8); break;
204 : case T_max: break;
205 : }
206 :
207 179492925 : if (/*!noValidation &&*/ isPosInCheck(p)) {
208 : STOP_AND_SUM_TIMER(Apply)
209 : return false; // this is the only legal move validation needed
210 : }
211 :
212 175492987 : const bool pawnMove = abs(moveInfo.fromP) == P_wp;
213 : // update EP
214 175492987 : if (p.ep != INVALIDSQUARE) p.h ^= Zobrist::ZT[p.ep][13];
215 175492987 : p.ep = INVALIDSQUARE;
216 209330229 : if (pawnMove && abs(moveInfo.to - moveInfo.from) == 16 && (BBTools::adjacent(SquareToBitboard(moveInfo.to)) & p.pieces_const(~p.c,P_wp)) != emptyBitBoard) {
217 1125149 : p.ep = static_cast<Square>((moveInfo.from + moveInfo.to) / 2);
218 1125149 : p.h ^= Zobrist::ZT[p.ep][13];
219 : }
220 175492987 : assert(p.ep == INVALIDSQUARE || SQRANK(p.ep) == EPRank[~p.c]);
221 :
222 : // update Color
223 175492987 : p.c = ~p.c;
224 175492987 : p.h ^= Zobrist::ZT[3][13];
225 175492987 : p.h ^= Zobrist::ZT[4][13];
226 :
227 : // update game state
228 175492987 : if (moveInfo.isCapNoEP || pawnMove) p.fifty = 0; // E.p covered by pawn move here ...
229 : else
230 86271261 : ++p.fifty;
231 175492987 : if (p.c == Co_White) ++p.moves;
232 175492987 : ++p.halfmoves;
233 :
234 175492987 : if (isCaptureOrProm(moveInfo.type)) MaterialHash::updateMaterialOther(p);
235 :
236 : #ifdef WITH_NNUE
237 175492987 : if (!noNNUEUpdate){
238 161409957 : applyMoveNNUEUpdate(p, moveInfo);
239 : }
240 : #endif
241 :
242 : #ifdef DEBUG_MATERIAL
243 : const Position::Material mat = p.mat;
244 : MaterialHash::initMaterial(p);
245 : if (p.mat != mat) {
246 : Logging::LogIt(Logging::logWarn) << "Material update error";
247 : Logging::LogIt(Logging::logWarn) << "Material previous " << ToString(previous) << ToString(previous.mat);
248 : Logging::LogIt(Logging::logWarn) << "Material computed " << ToString(p) << ToString(p.mat);
249 : Logging::LogIt(Logging::logWarn) << "Material incrementally updated " << ToString(mat);
250 : Logging::LogIt(Logging::logFatal) << "Last move " << ToString(p.lastMove) << " current move " << ToString(moveInfo.m);
251 : }
252 : #endif
253 :
254 : #ifdef DEBUG_BITBOARD
255 : int count_bb1 = BB::countBit(p.occupancy());
256 : int count_bb2 = 0;
257 : int count_board = 0;
258 : for (Square s = Sq_a1; s <= Sq_h8; ++s) {
259 : if (p.board_const(s) != P_none) ++count_board;
260 : }
261 : for (Piece pp = P_bk; pp <= P_wk; ++pp) {
262 : if (pp == P_none) continue;
263 : BitBoard b = p.pieces_const(pp);
264 : const BitBoard bb = b;
265 : applyOn(b, [&](const Square & k){ {
266 : ++count_bb2;
267 : if (p.board_const(k) != pp) {
268 : Logging::LogIt(Logging::logWarn) << SquareNames[k];
269 : Logging::LogIt(Logging::logWarn) << ToString(p);
270 : Logging::LogIt(Logging::logWarn) << ToString(bb);
271 : Logging::LogIt(Logging::logWarn) << static_cast<int>(pp);
272 : Logging::LogIt(Logging::logWarn) << static_cast<int>(p.board_const(k));
273 : Logging::LogIt(Logging::logWarn) << "last move " << ToString(p.lastMove);
274 : Logging::LogIt(Logging::logWarn) << " current move " << ToString(moveInfo.m);
275 : Logging::LogIt(Logging::logFatal) << "Wrong bitboard ";
276 : }
277 : });
278 : }
279 : if (count_bb1 != count_bb2) {
280 : Logging::LogIt(Logging::logFatal) << "Wrong bitboard count (all/piece)" << count_bb1 << " " << count_bb2;
281 : Logging::LogIt(Logging::logWarn) << ToString(p);
282 : Logging::LogIt(Logging::logWarn) << ToString(p.occupancy());
283 : for (Piece pp = P_bk; pp <= P_wk; ++pp) {
284 : if (pp == P_none) continue;
285 : Logging::LogIt(Logging::logWarn) << ToString(p.pieces_const(pp));
286 : }
287 : }
288 : if (count_board != count_bb1) {
289 : Logging::LogIt(Logging::logFatal) << "Wrong bitboard count (board)" << count_board << " " << count_bb1;
290 : Logging::LogIt(Logging::logWarn) << ToString(p);
291 : Logging::LogIt(Logging::logWarn) << ToString(p.occupancy());
292 : }
293 : #endif
294 175492987 : p.lastMove = Move2MiniMove(moveInfo.m);
295 : STOP_AND_SUM_TIMER(Apply)
296 175492987 : return true;
297 : }
298 :
299 166812699 : void applyMoveNNUEUpdate([[maybe_unused]] Position & p, [[maybe_unused]] const MoveInfo & moveInfo){
300 : #ifdef WITH_NNUE
301 166812699 : if (!DynamicConfig::useNNUE) return;
302 : // if king is not moving, update nnue evaluator
303 : // this is based on initial position state (most notably ep square),
304 : // all is available in the previously built moveInfo)
305 166812699 : if (Abs(moveInfo.fromP) != P_wk || (getBlock(moveInfo.from) == getBlock(moveInfo.to))) {
306 : // ***be carefull here***, p.c has already been updated !!!
307 185851172 : if (p.c == Co_Black) updateNNUEEvaluator<Co_White>(p.evaluator(), moveInfo);
308 130788956 : else updateNNUEEvaluator<Co_Black>(p.evaluator(), moveInfo);
309 : }
310 : // if a king was moved (including castling!!), reset nnue evaluator
311 : // this is based on new position state !
312 : else {
313 8492635 : p.resetNNUEEvaluator(p.evaluator(), ~p.c);
314 8492635 : if (isCastling(moveInfo.type)){
315 139703 : p.resetNNUEEvaluator(p.evaluator(), p.c);
316 : }
317 : else{
318 : // ***be carefull here***, p.c has already been updated !!!
319 9948940 : if (p.c == Co_Black) updateNNUEEvaluatorThemOnly<Co_White>(p.evaluator(), moveInfo);
320 6756924 : else updateNNUEEvaluatorThemOnly<Co_Black>(p.evaluator(), moveInfo);
321 : }
322 : }
323 :
324 : #ifdef DEBUG_NNUE_UPDATE
325 : Position p2 = p;
326 : NNUEEvaluator evaluator;
327 : p2.associateEvaluator(evaluator);
328 : p2.resetNNUEEvaluator(p2.evaluator());
329 : if (p2.evaluator() != p.evaluator()) {
330 : Logging::LogIt(Logging::logWarn) << "evaluator update error";
331 : Logging::LogIt(Logging::logWarn) << ToString(p);
332 : Logging::LogIt(Logging::logWarn) << ToString(moveInfo.m);
333 : Logging::LogIt(Logging::logWarn) << ToString(p.lastMove);
334 : Logging::LogIt(Logging::logWarn) << p2.evaluator().white.active();
335 : Logging::LogIt(Logging::logWarn) << p2.evaluator().black.active();
336 : Logging::LogIt(Logging::logWarn) << "--------------------";
337 : Logging::LogIt(Logging::logWarn) << p.evaluator().white.active();
338 : Logging::LogIt(Logging::logWarn) << p.evaluator().black.active();
339 : Logging::LogIt(Logging::logWarn) << backtrace();
340 : }
341 : #endif // DEBUG_NNUE_UPDATE
342 :
343 : #endif // WITH_NNUE
344 :
345 :
346 : }
|