Line data Source code
1 : /*
2 : * Copyright (C) 2013-2015 Fulvio Benini
3 :
4 : * This file is part of Scid (Shane's Chess Information Database).
5 : *
6 : * Scid is free software: you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation.
9 : *
10 : * Scid is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : * GNU General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU General Public License
16 : * along with Scid. If not, see <http://www.gnu.org/licenses/>.
17 : */
18 :
19 : #ifndef FASTGAME_H
20 : #define FASTGAME_H
21 :
22 : #include "common.h"
23 : #include "fullmove.h"
24 : #include "movegen.h"
25 : #include "position.h"
26 : #include <algorithm>
27 : #include <cstdlib>
28 : #include <cstring>
29 : #include <sstream>
30 : #include <string>
31 :
32 : /// Store the number of pieces for each type and color.
33 34 : class MaterialCount {
34 : int8_t n_[2][8] = {};
35 :
36 : public:
37 : /// Add one piece.
38 457 : void incr(colorT color, pieceT piece_type) {
39 457 : ASSERT(color == 0 || color == 1);
40 457 : ASSERT(piece_type > 0 && piece_type < 8);
41 :
42 457 : ++n_[color][0];
43 457 : ++n_[color][piece_type];
44 457 : }
45 :
46 : /// Subtract one piece.
47 6 : void decr(colorT color, pieceT piece_type) {
48 6 : ASSERT(color == 0 || color == 1);
49 6 : ASSERT(piece_type > 0 && piece_type < 8);
50 :
51 6 : --n_[color][0];
52 6 : --n_[color][piece_type];
53 6 : }
54 :
55 : /// Return the total number of pieces of the specified color.
56 133 : int8_t count(colorT color) const {
57 133 : ASSERT(color == 0 || color == 1);
58 :
59 133 : return n_[color][0];
60 : }
61 :
62 : /// Return the number of pieces of the specified color and type.
63 164 : int8_t count(colorT color, pieceT piece_type) const {
64 164 : ASSERT(color == 0 || color == 1);
65 164 : ASSERT(piece_type > 0 && piece_type < 8);
66 :
67 164 : return n_[color][piece_type];
68 : }
69 :
70 6 : bool operator==(const MaterialCount& b) const {
71 6 : const int8_t* a = n_[0];
72 6 : const int8_t* b_ptr = b.n_[0];
73 6 : return std::equal(a, a + 16, b_ptr);
74 : }
75 :
76 3 : bool operator!=(const MaterialCount& b) const {
77 3 : return !operator==(b);
78 : }
79 : };
80 :
81 : /// Store the type and position of the pieces compatibly with the SCID4 coding.
82 : class PieceList {
83 : struct {
84 : squareT sq;
85 : pieceT piece_type;
86 : } pieces_[2][16];
87 :
88 : public:
89 : /// SCID4 encoded games must use index 0 for kings.
90 129 : int8_t getKingIdx() const { return 0; }
91 :
92 : /// Return the type of the piece with index @e idx
93 366 : pieceT getPieceType(colorT color, int idx) const {
94 366 : ASSERT(color == 0 || color == 1);
95 366 : ASSERT(idx >= 0 && idx < 16);
96 :
97 366 : return pieces_[color][idx].piece_type;
98 : }
99 :
100 : /// Return the square position of the piece with index @e idx
101 208 : squareT getSquare(colorT color, int idx) const {
102 208 : ASSERT(color == 0 || color == 1);
103 208 : ASSERT(idx >= 0 && idx < 16);
104 :
105 208 : return pieces_[color][idx].sq;
106 : }
107 :
108 : /// Change the square position of the piece with index @e idx
109 0 : void move(colorT color, int idx, squareT to) {
110 0 : ASSERT(color == 0 || color == 1);
111 0 : ASSERT(idx >= 0 && idx < 16);
112 :
113 0 : pieces_[color][idx].sq = to;
114 0 : }
115 :
116 : /// Change the type of the piece with index @e idx
117 0 : void promote(colorT color, int idx, pieceT piece_type) {
118 0 : ASSERT(color == 0 || color == 1);
119 0 : ASSERT(idx >= 0 && idx < 16);
120 :
121 0 : pieces_[color][idx].piece_type = piece_type;
122 0 : }
123 :
124 : /// Remove the piece with index @e removed_idx.
125 : /// Piece's indexes are important for decoding SCID4 moves: when a piece is
126 : /// removed it's index is used by the last valid index @e lastvalid_idx.
127 : /// Return the square of the new piece with index @e removed_idx.
128 0 : squareT remove(colorT color, int removed_idx, int lastvalid_idx) {
129 0 : ASSERT(color == 0 || color == 1);
130 0 : ASSERT(removed_idx >= 0 && removed_idx < 16);
131 0 : ASSERT(lastvalid_idx >= 0 && lastvalid_idx < 16);
132 :
133 0 : pieces_[color][removed_idx] = pieces_[color][lastvalid_idx];
134 0 : return pieces_[color][lastvalid_idx].sq;
135 : }
136 :
137 : /// Set the type and square of the piece with index @e idx
138 1088 : void set(colorT color, int idx, squareT sq, pieceT piece_type) {
139 1088 : ASSERT(color == 0 || color == 1);
140 1088 : ASSERT(idx >= 0 && idx < 16);
141 1088 : ASSERT(piece_type != KING || idx == getKingIdx());
142 :
143 1088 : pieces_[color][idx].sq = sq;
144 1088 : pieces_[color][idx].piece_type = piece_type;
145 1088 : }
146 : };
147 :
148 : class FastBoard {
149 : uint8_t board_[64];
150 : MaterialCount mt_;
151 : PieceList pieces_;
152 :
153 : enum { EMPTY_SQ_ = 0xFF };
154 :
155 : public:
156 0 : FastBoard() {}
157 34 : FastBoard(Position& pos) { Init(pos); }
158 :
159 0 : void Init() {
160 0 : static Position StdStartPos(Position::getStdStart());
161 0 : static FastBoard StdStart(StdStartPos);
162 0 : *this = StdStart;
163 0 : }
164 :
165 34 : void Init(Position& pos) {
166 34 : std::fill_n(board_, 64, EMPTY_SQ_);
167 :
168 102 : for (auto color : {WHITE, BLACK}) {
169 68 : const auto pos_count = pos.GetCount(color);
170 68 : const auto pos_list = pos.GetList(color);
171 1156 : for (uint8_t idx = 0; idx < 16; ++idx) {
172 1088 : if (idx < pos_count) {
173 415 : const squareT sq = pos_list[idx];
174 415 : const pieceT piece_type = piece_Type(pos.GetPiece(sq));
175 415 : pieces_.set(color, idx, sq, piece_type);
176 415 : board_[sq] = idx;
177 415 : mt_.incr(color, piece_type);
178 : } else {
179 673 : pieces_.set(color, idx, 0, INVALID_PIECE);
180 : }
181 : }
182 : }
183 34 : }
184 :
185 : bool isEqual(const pieceT* board, const MaterialCount& mt_count) const {
186 : if (mt_ != mt_count)
187 : return false;
188 :
189 : for (int idx = 0, n = mt_.count(WHITE); idx < n; ++idx) {
190 : const auto sq = pieces_.getSquare(WHITE, idx);
191 : const auto pt = pieces_.getPieceType(WHITE, idx);
192 : if (board[sq] != piece_Make(WHITE, pt))
193 : return false;
194 : }
195 : for (int idx = 0, n = mt_.count(BLACK); idx < n; ++idx) {
196 : const auto sq = pieces_.getSquare(BLACK, idx);
197 : const auto pt = pieces_.getPieceType(BLACK, idx);
198 : if (board[sq] != piece_Make(BLACK, pt))
199 : return false;
200 : }
201 : return true;
202 : }
203 :
204 : const MaterialCount& materialCount() const {
205 : return mt_;
206 : }
207 :
208 147 : squareT getSquare(colorT color, int idx) const {
209 147 : return pieces_.getSquare(color, idx);
210 : }
211 :
212 366 : pieceT getPiece(colorT color, int idx) const {
213 366 : return pieces_.getPieceType(color, idx);
214 : }
215 :
216 : // TODO: error detection
217 0 : template <colorT color> squareT castle(bool king_side) {
218 0 : const squareT black = (color == WHITE) ? 0 : 56;
219 : squareT king_to, rook_from, rook_to;
220 0 : if (king_side) { // King Side
221 0 : king_to = black + G1;
222 0 : rook_from = black + H1;
223 0 : rook_to = black + F1;
224 : } else { // Queen Side
225 0 : king_to = black + C1;
226 0 : rook_from = black + A1;
227 0 : rook_to = black + D1;
228 : }
229 0 : const uint8_t rook_idx = board_[rook_from];
230 0 : const auto king_idx = pieces_.getKingIdx();
231 0 : const squareT king_from = pieces_.getSquare(color, king_idx);
232 0 : pieces_.move(color, rook_idx, rook_to);
233 0 : pieces_.move(color, king_idx, king_to);
234 0 : board_[rook_to] = rook_idx;
235 0 : board_[king_to] = king_idx;
236 0 : board_[rook_from] = EMPTY_SQ_;
237 0 : board_[king_from] = EMPTY_SQ_;
238 0 : return rook_from;
239 : }
240 :
241 : template <colorT color>
242 0 : pieceT move(uint8_t idx, squareT to, pieceT promo) {
243 0 : if (promo != INVALID_PIECE) {
244 0 : pieces_.promote(color, idx, promo);
245 0 : mt_.incr(color, promo);
246 0 : mt_.decr(color, PAWN);
247 : }
248 0 : const auto from = pieces_.getSquare(color, idx);
249 0 : board_[from] = EMPTY_SQ_;
250 0 : pieces_.move(color, idx, to);
251 0 : return remove<1 - color>(to, idx);
252 : }
253 :
254 : template <colorT color>
255 0 : pieceT remove(squareT sq, uint8_t newIdx = EMPTY_SQ_) {
256 0 : const uint8_t oldIdx = board_[sq];
257 0 : board_[sq] = newIdx;
258 0 : if (oldIdx == EMPTY_SQ_)
259 0 : return INVALID_PIECE;
260 :
261 0 : pieceT removed_pt = pieces_.getPieceType(color, oldIdx);
262 0 : mt_.decr(color, removed_pt);
263 0 : int lastvalid_idx = mt_.count(color);
264 0 : if (oldIdx != lastvalid_idx) {
265 0 : squareT moved_sq = pieces_.remove(color, oldIdx, lastvalid_idx);
266 0 : board_[moved_sq] = oldIdx;
267 : }
268 0 : return removed_pt;
269 : }
270 :
271 : /**
272 : * Given the actual board position, find if the last move needs to be made
273 : * unambiguous and if it gives check (or TODO mate), and then sets the
274 : * appropriate bits in @e lastmove.
275 : * @param lastmove: the last move played.
276 : */
277 34 : void fillSANInfo(FullMove& lastmove) {
278 34 : squareT lastFrom = lastmove.getFrom();
279 34 : squareT lastTo = lastmove.getTo();
280 34 : colorT lastCol = lastmove.getColor();
281 34 : pieceT lastPt = lastmove.getPiece();
282 :
283 34 : if (lastPt == PAWN) {
284 6 : if (lastmove.isPromo())
285 5 : lastPt = lastmove.getPromo();
286 28 : } else if (mt_.count(lastCol, lastPt) > 1) {
287 27 : int ambiguity = ambiguousMove(lastFrom, lastTo, lastCol, lastPt);
288 27 : if (ambiguity)
289 21 : lastmove.setAmbiguity(ambiguity != 5, ambiguity >= 5);
290 : }
291 :
292 : // Look for checks
293 34 : ASSERT(mt_.count(WHITE) >= 1 && mt_.count(BLACK) >= 1);
294 :
295 34 : const squareT enemyKingSq = getKingSquare(color_Flip(lastCol));
296 34 : bool direct_check = lastPt != KING && movegen::attack<uint8_t>(
297 : lastTo, enemyKingSq, lastCol,
298 34 : lastPt, board_, EMPTY_SQ_);
299 60 : if (direct_check || // Look for a discovered check
300 26 : find_attacker_slider(enemyKingSq, lastCol) >= 0) {
301 10 : lastmove.setCheck();
302 :
303 : // TODO: Find if it's mate:
304 : // - it's not mate if the king can move to a safe square
305 : // - it's mate if it's double check or the attacker cannot be
306 : // captured or blocked.
307 : }
308 34 : }
309 :
310 : private:
311 61 : squareT getKingSquare(colorT color) {
312 61 : return pieces_.getSquare(color, pieces_.getKingIdx());
313 : }
314 :
315 27 : int ambiguousMove(squareT lastFrom, squareT lastTo, colorT lastCol,
316 : pieceT lastPt) {
317 27 : int ambiguity = 0;
318 :
319 27 : const squareT kingSq = getKingSquare(lastCol);
320 27 : const colorT enemyCol = color_Flip(lastCol);
321 195 : for (int i = 1, n = mt_.count(lastCol); i < n; i++) {
322 168 : if (getPiece(lastCol, i) != lastPt)
323 232 : continue; // Skip: different type
324 :
325 71 : const squareT sq = getSquare(lastCol, i);
326 71 : if (sq == lastTo)
327 27 : continue; // Skip: this is the analyzed piece
328 :
329 44 : board_[lastFrom] = board_[sq];
330 44 : board_[sq] = EMPTY_SQ_;
331 :
332 44 : bool pseudoLegal = movegen::pseudo<uint8_t>(
333 44 : sq, lastTo, lastCol, lastPt, board_, EMPTY_SQ_);
334 :
335 44 : std::pair<pieceT, squareT> pin;
336 44 : if (pseudoLegal)
337 35 : pin = movegen::opens_ray<uint8_t>(sq, lastTo, kingSq, board_,
338 35 : EMPTY_SQ_);
339 44 : board_[sq] = board_[lastFrom];
340 44 : board_[lastFrom] = EMPTY_SQ_;
341 :
342 44 : if (!pseudoLegal)
343 9 : continue; // Skip: illegal move
344 :
345 35 : if (pin.first != INVALID_PIECE) {
346 4 : uint8_t idx = board_[pin.second];
347 8 : if (idx != EMPTY_SQ_ && idx < mt_.count(enemyCol) &&
348 4 : getSquare(enemyCol, idx) == pin.second) {
349 4 : const pieceT pt = getPiece(enemyCol, idx);
350 4 : if (pt == QUEEN || pt == pin.first)
351 2 : continue; // Skip: pinned piece
352 : }
353 : }
354 :
355 : // Ambiguity:
356 : // 1 (0001) --> need from-file (preferred) or from-rank
357 : // 3 (0011) --> need from-file
358 : // 5 (0101) --> need from-rank
359 : // 7 (0111) --> need both from-file and from-rank
360 33 : ambiguity |= 1;
361 33 : if (square_Rank(lastFrom) == square_Rank(sq)) {
362 10 : ambiguity |= 2; // 0b0010
363 23 : } else if (square_Fyle(lastFrom) == square_Fyle(sq)) {
364 6 : ambiguity |= 4; // 0b0100
365 : }
366 : }
367 :
368 27 : return ambiguity;
369 : }
370 :
371 26 : int find_attacker_slider(squareT destSq, colorT color) {
372 218 : for (int idx = 0, n = mt_.count(color); idx < n; ++idx) {
373 194 : const pieceT pt = getPiece(color, idx);
374 194 : if (pt != QUEEN && pt != ROOK && pt != BISHOP)
375 122 : continue;
376 :
377 72 : const squareT sq = getSquare(color, idx);
378 72 : if (movegen::attack_slider<uint8_t>(sq, destSq, pt, board_,
379 : EMPTY_SQ_)) {
380 2 : return idx;
381 : }
382 : }
383 24 : return -1;
384 : }
385 : };
386 :
387 : class FastGame {
388 : FastBoard board_;
389 : const byte* v_it_;
390 : const byte* v_end_;
391 : colorT cToMove_;
392 :
393 : public:
394 0 : static FastGame Create(const byte* v_begin, const byte* v_end) {
395 0 : const byte* v_it = v_begin;
396 0 : while (v_it < v_end) {
397 0 : byte b = *v_it++;
398 0 : if (b == 0) {
399 0 : if (v_it >= v_end) break; // Error
400 0 : byte haveFEN = *v_it++ & 1;
401 0 : if (haveFEN == 0) {
402 0 : return FastGame(v_it, v_end);
403 : } else {
404 0 : const char* FENstring = (char*) v_it;
405 0 : while (v_it < v_end) {
406 0 : if (*v_it++ == 0) return FastGame(FENstring, v_it, v_end);
407 : }
408 0 : break; // FEN error
409 : }
410 0 : } else if (b == 255) { // Skip special 3-byte binary encoding of EventDate
411 0 : v_it += 3;
412 : } else { // Skip tags
413 : enum { MAX_TAG_LEN = 240 };
414 0 : if (b <= MAX_TAG_LEN) v_it += b;
415 0 : if (v_it < v_end) v_it += *v_it +1;
416 : }
417 : }
418 :
419 0 : return FastGame(0,0); // Error default to StdStart and empty buffer
420 : }
421 :
422 0 : FullMove getMove(int ply_to_skip) {
423 0 : for (int ply=0; ply <= ply_to_skip; ply++, cToMove_ = 1 - cToMove_) {
424 0 : auto move = (cToMove_ == WHITE)
425 : ? DecodeNextMove<FullMove, WHITE>()
426 0 : : DecodeNextMove<FullMove, BLACK>();
427 0 : if (!move)
428 0 : break;
429 :
430 0 : if (ply == ply_to_skip) {
431 0 : board_.fillSANInfo(move);
432 0 : cToMove_ = 1 - cToMove_;
433 0 : return move;
434 : }
435 : }
436 0 : return {};
437 : }
438 :
439 : std::string getMoveSAN(int ply_to_skip, int count) {
440 : std::stringstream res;
441 : for (int ply=0; ply < ply_to_skip + count; ply++, cToMove_ = 1 - cToMove_) {
442 : FullMove move;
443 : if (cToMove_ == WHITE) {
444 : move = DecodeNextMove <FullMove, WHITE>();
445 : if (!move)
446 : break;
447 : if (ply < ply_to_skip) continue;
448 : if (ply > ply_to_skip) res << " ";
449 : res << (1 + ply/2) << ".";
450 : } else {
451 : move = DecodeNextMove <FullMove, BLACK>();
452 : if (!move)
453 : break;
454 : if (ply < ply_to_skip) continue;
455 : if (ply == ply_to_skip) res << (1 + ply/2) << "...";
456 : else res << " ";
457 : }
458 : board_.fillSANInfo(move);
459 : res << move.getSAN();
460 : }
461 : return res.str();
462 : }
463 :
464 : template <colorT toMove>
465 : int search(const byte* board, const MaterialCount& mt_count) {
466 : int ply = 1;
467 : auto less_material = [](const MaterialCount& a, const MaterialCount& b,
468 : const colorT color, const auto move) {
469 : if (!move)
470 : return true;
471 :
472 : const auto captured_pt = move.getCaptured();
473 : if (captured_pt == INVALID_PIECE)
474 : return false;
475 :
476 : if (a.count(color) < b.count(color))
477 : return true;
478 :
479 : return a.count(color, PAWN) + a.count(color, captured_pt) <
480 : b.count(color, PAWN) + b.count(color, captured_pt);
481 : };
482 :
483 : if (cToMove_ != toMove) {
484 : const auto move = DecodeNextMove<FullMove, 1 - toMove>();
485 : if (!move)
486 : return 0;
487 : ply += 1;
488 : }
489 : for (;;) {
490 : if (board_.isEqual(board, mt_count))
491 : return ply;
492 :
493 : {
494 : const auto move = DecodeNextMove<FullMove, toMove>();
495 : if (less_material(board_.materialCount(), mt_count, 1 - toMove,
496 : move))
497 : return 0;
498 : }
499 : {
500 : const auto move = DecodeNextMove<FullMove, 1 - toMove>();
501 : if (less_material(board_.materialCount(), mt_count, toMove,
502 : move))
503 : return 0;
504 : }
505 :
506 : ply += 2;
507 : }
508 : return 0;
509 : }
510 :
511 : private:
512 0 : FastGame(const byte* v_it, const byte* v_end)
513 0 : : v_it_ (v_it), v_end_(v_end), cToMove_(WHITE) {
514 0 : board_.Init();
515 0 : }
516 :
517 0 : FastGame(const char* FEN, const byte* v_it, const byte* v_end)
518 0 : : v_it_ (v_it), v_end_(v_end) {
519 0 : Position StartPos;
520 0 : if (FEN == 0 || StartPos.ReadFromFEN(FEN) != OK) StartPos.StdStart();
521 0 : board_.Init(StartPos);
522 0 : cToMove_ = StartPos.GetToMove();
523 0 : }
524 :
525 : template <typename TResult, colorT toMove>
526 0 : TResult DecodeNextMove() {
527 : enum { ENCODE_NAG = 11, ENCODE_COMMENT, ENCODE_START_MARKER, ENCODE_END_MARKER, ENCODE_END_GAME };
528 : enum { ENCODE_FIRST = 11, ENCODE_LAST = 15 };
529 :
530 0 : while (v_it_ < v_end_) {
531 0 : byte b = *v_it_++;
532 0 : if (b < ENCODE_FIRST || b > ENCODE_LAST) return doPly<TResult, toMove>(b);
533 0 : if (b == ENCODE_END_GAME || b == ENCODE_END_MARKER) return {};
534 0 : if (b == ENCODE_NAG) {v_it_++; continue; }
535 0 : if (b == ENCODE_START_MARKER) {
536 0 : int nestCount = 1;
537 0 : do {
538 0 : if (v_it_ >= v_end_) return {};
539 0 : switch (*v_it_++) {
540 0 : case ENCODE_NAG: v_it_++; break;
541 0 : case ENCODE_START_MARKER: nestCount++; break;
542 0 : case ENCODE_END_MARKER: nestCount--; break;
543 0 : case ENCODE_END_GAME: return {};
544 : }
545 0 : } while (nestCount > 0);
546 : }
547 : }
548 0 : return {};
549 : }
550 :
551 0 : template <typename TResult, colorT toMove> TResult doPly(byte v) {
552 0 : byte idx_piece_moving = v >> 4;
553 0 : byte move = v & 0x0F;
554 0 : pieceT moving_piece = board_.getPiece(toMove, idx_piece_moving);
555 0 : squareT from = board_.getSquare(toMove, idx_piece_moving);
556 : int to;
557 0 : pieceT promo = INVALID_PIECE;
558 0 : bool enPassant = false;
559 0 : switch (moving_piece) {
560 0 : case PAWN:
561 0 : to = decodePawn<toMove>(from, move, promo, enPassant);
562 0 : break;
563 0 : case BISHOP:
564 0 : to = decodeBishop(from, move);
565 0 : break;
566 0 : case KNIGHT:
567 0 : to = decodeKnight(from, move);
568 0 : break;
569 0 : case QUEEN:
570 0 : if (move == square_Fyle(from)) { // 2 BYTES MOVE
571 0 : if (v_it_ >= v_end_)
572 0 : return {}; // decode error
573 :
574 0 : to = decodeQueen2byte(*v_it_++);
575 0 : break;
576 : }
577 : /* FALLTHRU */
578 : case ROOK:
579 0 : to = decodeRook(from, move);
580 0 : break;
581 0 : case KING:
582 0 : if (move == 0) { // NULL MOVE
583 0 : return TResult(toMove, 0, 0, KING);
584 : }
585 0 : if (move <= 8) {
586 0 : to = decodeKing(from, move);
587 0 : break;
588 : }
589 0 : if (move <= 10) { // CASTLE
590 0 : const squareT rook_from = board_.castle<toMove>(move == 10);
591 0 : return TResult(toMove, from, rook_from);
592 : }
593 0 : return {}; // decode error
594 :
595 0 : default:
596 0 : return {}; // decode error
597 : }
598 :
599 0 : if (to < 0 || to > 63)
600 0 : return {}; // decode error
601 :
602 0 : pieceT captured = board_.move<toMove>(idx_piece_moving, to, promo);
603 0 : TResult res(toMove, from, to, moving_piece);
604 0 : if (promo != INVALID_PIECE)
605 0 : res.setPromo(promo);
606 0 : if (captured != INVALID_PIECE) {
607 0 : res.setCapture(captured, false);
608 0 : } else if (enPassant) {
609 0 : squareT sq = (toMove == WHITE) ? to - 8 : to + 8;
610 0 : captured = board_.remove<1 - toMove>(0x3F & sq);
611 0 : res.setCapture(captured, true);
612 : }
613 0 : return res;
614 : }
615 :
616 : /**
617 : * decode*() - decode a move from Scid format
618 : * @from: start square of the moving piece
619 : * @val: index of the target square
620 : *
621 : * Excluding queens, the other chess pieces cannot reach more than 16 target
622 : * squares from any given position. This allow to store the target square of
623 : * a move into 4 bits, as an index of all the possible target squares.
624 : * Return:
625 : * - the target square
626 : * Error handling:
627 : * - Debug code will check if the decoded value is a valid [0-63] square.
628 : * - Release code will force the returned valid to be a valid [0-63] square
629 : * but, for performance reasons, do not report invalid encoded moves.
630 : */
631 0 : static inline int decodeKing(squareT from, byte val) {
632 0 : ASSERT(val <= 8);
633 : static const int8_t sqdiff[] = {0, -9, -8, -7, -1, 1, 7, 8, 9};
634 0 : return from + sqdiff[val];
635 : }
636 0 : static inline int decodeQueen2byte(byte val) {
637 0 : return val - 64;
638 : }
639 0 : static inline int decodeBishop(squareT from, byte val) {
640 0 : int fylediff = square_Fyle(val) - square_Fyle(from);
641 0 : return (val >= 8) ? from - 7 * fylediff //
642 0 : : from + 9 * fylediff;
643 : }
644 0 : static inline int decodeKnight(squareT from, byte val) {
645 0 : ASSERT(val <= 16);
646 : static const int8_t sqdiff[] = {0, -17, -15, -10, -6, 6, 10, 15, 17, 0, 0, 0, 0, 0, 0, 0};
647 0 : return from + sqdiff[val];
648 : }
649 0 : static inline int decodeRook(squareT from, byte val) {
650 0 : ASSERT(val <= 16);
651 0 : if (val >= 8) // vertical move
652 0 : return square_Make(square_Fyle(from), (val - 8));
653 : else // horizontal move
654 0 : return square_Make(val, square_Rank(from));
655 : }
656 : template <colorT color>
657 0 : static inline int decodePawn(squareT from, byte val, pieceT& promo,
658 : bool& enPassant) {
659 0 : ASSERT(val <= 16);
660 : static const int8_t sqdiff [] = { 7,8,9, 7,8,9, 7,8,9, 7,8,9, 7,8,9, 16 };
661 : static const pieceT promoPieceFromVal [] = {
662 : 0,0,0,QUEEN,QUEEN,QUEEN, ROOK,ROOK,ROOK, BISHOP,BISHOP,BISHOP,KNIGHT,KNIGHT,KNIGHT,0
663 : };
664 0 : promo = promoPieceFromVal[val];
665 0 : enPassant = (val == 0 || val == 2);
666 0 : return (color == WHITE) ? from + sqdiff[val] //
667 0 : : from - sqdiff[val];
668 : }
669 : };
670 :
671 :
672 : #endif
|