LCOV - code coverage report
Current view: top level - src - fastgame.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 127 319 39.8 %
Date: 2019-01-29 11:06:41 Functions: 19 45 42.2 %

          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

Generated by: LCOV version 1.13