LCOV - code coverage report
Current view: top level - src - game.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 892 1871 47.7 %
Date: 2019-01-29 11:06:41 Functions: 62 87 71.3 %

          Line data    Source code
       1             : //////////////////////////////////////////////////////////////////////
       2             : //
       3             : //  FILE:       game.cpp
       4             : //              Game class methods
       5             : //
       6             : //  Part of:    Scid (Shane's Chess Information Database)
       7             : //  Version:    3.5
       8             : //
       9             : //  Notice:     Copyright (c) 2000-2003  Shane Hudson.  All rights reserved.
      10             : //
      11             : //  Author:     Shane Hudson (sgh@users.sourceforge.net)
      12             : //
      13             : //////////////////////////////////////////////////////////////////////
      14             : 
      15             : #include "game.h"
      16             : #include "bytebuf.h"
      17             : #include "common.h"
      18             : #include "dstring.h"
      19             : #include "naglatex.h"
      20             : #include "nagtext.h"
      21             : #include "position.h"
      22             : #include "stored.h"
      23             : #include "textbuf.h"
      24             : #include <algorithm>
      25             : #include <cstring>
      26             : 
      27             : // Piece letters translation
      28             : int language = 0; // default to english
      29             : //  0 = en, 
      30             : //  1 = fr, 2 = es, 3 = de, 4 = it, 5 = ne, 6 = cz
      31             : //  7 = hu, 8 = no, 9 = sw, 10 = ca, 11 = fi, 12 = gr
      32             : //  TODO Piece translations for greek
      33             : const char * langPieces[] = { "", 
      34             : "PPKRQDRTBFNC", "PPKRQDRTBANC", "PBKKQDRTBLNS", 
      35             : "PPKRQDRTBANC", "PpKKQDRTBLNP", "PPKKQDRVBSNJ",
      36             : "PGKKQVRBBFNH", "PBKKQDRTBLNS", "PBKKQDRTBLNS", "PPKRQDRTBANC", "PSKKQDRTBLNR", "" };
      37             : 
      38             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      39             : // transPieces():
      40             : // Given a string, will translate pieces from english to another language
      41     1561972 : void transPieces(char *s) {
      42     1561972 :   if (language == 0) return;
      43           0 :   char * ptr = s;
      44             :   int i;
      45             : 
      46           0 :   while (*ptr) {
      47           0 :     if (*ptr >= 'A' && *ptr <= 'Z') {
      48           0 :       for (i=0; i<12; i+=2) {
      49           0 :         if (*ptr == langPieces[language][i]) {
      50           0 :           *ptr = langPieces[language][i+1];
      51           0 :           break;
      52             :         }
      53             :       }
      54             :     }
      55           0 :     ptr++;
      56             :   }
      57             : }
      58             : 
      59           0 : char transPiecesChar(char c) {
      60           0 :   char ret = c;
      61           0 :   if (language == 0) return c;
      62           0 :   for (int i=0; i<12; i+=2) {
      63           0 :     if (c == langPieces[language][i]) {
      64           0 :       ret = langPieces[language][i+1];
      65           0 :       break;
      66             :       }
      67             :     }
      68           0 :   return ret;
      69             : }
      70             : 
      71             : const char * ratingTypeNames [17] = {
      72             :     "Elo", "Rating", "Rapid", "ICCF", "USCF", "DWZ", "ECF",
      73             :     // Reserved for future use:
      74             :     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
      75             :     // End of array marker:
      76             :     NULL
      77             : };
      78             : 
      79             : uint
      80           0 : strGetRatingType (const char * name) {
      81           0 :     uint i = 0;
      82           0 :     while (ratingTypeNames[i] != NULL) {
      83           0 :         if (strEqual (name, ratingTypeNames[i])) { return i; }
      84           0 :         i++;
      85             :     }
      86           0 :     return 0;
      87             : }
      88             : 
      89             : typedef Game * GamePtr;
      90             : 
      91             : 
      92             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      93             : // printNag(): converts a numeric NAG to its string equivalent.
      94             : //    The parameter <str> should point to a string at least 10 bytes long.
      95             : // TODO
      96             : //    replace < and > in NAG codes by <lt> and <gt>
      97             : void
      98         288 : game_printNag (byte nag, char * str, bool asSymbol, gameFormatT format)
      99             : {
     100         288 :     ASSERT (str != NULL);
     101             : 
     102         288 :     if (nag == 0) {
     103           0 :         *str = 0;
     104           0 :         return;
     105             :     }
     106             : 
     107         288 :     if (nag >= (sizeof evalNagsRegular / sizeof (const char *))) {
     108           0 :         if (format == PGN_FORMAT_LaTeX) *str = 0;
     109           0 :         else sprintf (str, "$%u", nag);
     110           0 :         return;
     111             :     }
     112             : 
     113         288 :     if (asSymbol) {
     114           0 :        if (format == PGN_FORMAT_LaTeX) {
     115           0 :           strcpy (str, evalNagsLatex[nag]);
     116             :        } else {
     117           0 :          strcpy (str, evalNagsRegular[nag]);
     118             :        }
     119           0 :        if (nag == NAG_Diagram) {
     120           0 :           if (format == PGN_FORMAT_LaTeX) {
     121           0 :               strcpy (str, evalNagsLatex[nag]);
     122           0 :           } else if (format == PGN_FORMAT_HTML) {
     123           0 :               strcpy(str, "<i>(D)</i>");
     124             :           } else {
     125           0 :               str[0] = 'D'; str[1] = 0;
     126             :           }
     127             :        }
     128           0 :        return;
     129             :     } else {
     130         288 :     sprintf (str, "%s$%d", format == PGN_FORMAT_LaTeX ? "\\" : "", nag);
     131             :     }
     132             : }
     133             : 
     134             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     135             : // game_parseNag():
     136             : //      Parses an annotation symbol into its numeric equivalent.
     137             : //      Accepts numeric format ($51) or symbols such as
     138             : //      !, ?, +=, -/+, N, etc.
     139             : //
     140         386 : byte game_parseNag(std::pair<const char*, const char*> strview) {
     141         386 :     auto slen = std::distance(strview.first, strview.second);
     142         386 :     if (slen == 0 || slen > 7)
     143           1 :         return 0;
     144             : 
     145         385 :     char strbuf[8] = {0};
     146         385 :     std::copy_n(strview.first, slen, strbuf);
     147         385 :     const char* str = strbuf;
     148             : 
     149         385 :     if (*str == '$') {
     150         384 :         str++;
     151         384 :         return (byte) strGetUnsigned(str);
     152             :     }
     153           1 :     if ((*str <= '9'  &&  *str >= '0')) {
     154           0 :         return (byte) strGetUnsigned(str);
     155             :     }
     156             : 
     157           1 :     if (*str == '!') {
     158             :         // Must be "!", "!!", "!?", or invalid:
     159           0 :         str++;
     160           0 :         if (*str == 0) { return NAG_GoodMove; }            // !      $1
     161           0 :         if (*str == '!') { return NAG_ExcellentMove; }     // !!     $3
     162           0 :         if (*str == '?') { return NAG_InterestingMove; }   // !?     $5
     163           0 :         return 0;
     164             :     }
     165             : 
     166           1 :     if (*str == '?') {
     167             :         // Must be "?", "??", "?!", or invalid:
     168           0 :         str++;
     169           0 :         if (*str == 0) { return NAG_PoorMove; }            // ?      $2
     170           0 :         if (*str == '?') { return NAG_Blunder; }           // ??     $4
     171           0 :         if (*str == '!') { return NAG_DubiousMove; }       // ?!     $6
     172           0 :         return 0;
     173             :     }
     174             : 
     175           1 :     if (*str == '+') {
     176             :         // Must be "+=", "+/=", "+/-", "+-", "+--", "+>" or invalid:
     177           0 :         str++;
     178           0 :         if (*str == '=') { return NAG_WhiteSlight; }      // +=      $14
     179           0 :         if (*str == '-' && str[1] == 0) {                 // +-      $18
     180           0 :            return NAG_WhiteDecisive; }
     181           0 :         if (*str == '>') { return NAG_WithAttack; }       // +>      $40
     182           0 :         if (*str == '/'  &&  str[1] == '-') {             // +/-     $16
     183           0 :            return NAG_WhiteClear; }
     184           0 :         if (*str == '/'  &&  str[1] == '=') {             // +/=     $14
     185           0 :            return NAG_WhiteSlight; }
     186           0 :         if (*str == '-'  &&  str[1] == '-') {             // +--     $20
     187           0 :            return NAG_WhiteCrushing; }
     188           0 :         return 0;
     189             :     }
     190             : 
     191           1 :     if (*str == '=') {
     192             :         // Must be "=" (equal), "=+", "=/+", "=/&" or invalid:
     193           0 :         str++;
     194           0 :         if (*str == 0) { return NAG_Equal; }              // =       $10
     195           0 :         if (*str == '+') { return NAG_BlackSlight; }      // =+      $15
     196           0 :         if (*str == '/'  &&  str[1] == '+') {             // =/+     $15
     197           0 :            return NAG_BlackSlight; }
     198           0 :         if (*str == '/'  &&  str[1] == '&') {             // =/&     $44
     199           0 :            return NAG_Compensation; }
     200           0 :         return 0;
     201             :     }
     202             : 
     203           1 :     if (*str == '-') {
     204             :         // Must be "-+", "-/+" or "--+", "->":
     205           0 :         str++;
     206           0 :         if (*str == '+') { return NAG_BlackDecisive; }     // -+     $19
     207           0 :         if (*str == '>') { return NAG_WithBlackAttack; }   // ->     $41
     208           0 :         if (*str == '/'  &&  str[1] == '+') {              // -/+    $17
     209           0 :            return NAG_BlackClear; }
     210           0 :         if (*str == '-'  &&  str[1] == '+') {              // --+    $21
     211           0 :            return NAG_BlackCrushing; }
     212           0 :         if (*str == '-'  &&  str[1] == 0) {                // --     $210
     213           0 :            return NAG_See; }
     214           0 :         return 0;
     215             :     }
     216             : 
     217           1 :     if (*str == '/') {
     218             :         // Must be "/\" or "/"
     219           0 :         str++;
     220           0 :         if (*str == 0)    { return NAG_Diagonal; }         // /      $150
     221           0 :         if (*str == '\\') { return NAG_WithIdea; }         // Tri    $140
     222           0 :         return 0;
     223             :     }
     224             : 
     225           1 :     if (*str == 'R') {
     226             :         // Must be "R", "RR"
     227           0 :         str++;
     228           0 :         if (*str == 0)   { return NAG_VariousMoves; }      // R      $144
     229           0 :         if (*str == 'R') { return NAG_Comment; }           // RR     $145
     230           0 :         return 0;
     231             :     }
     232             : 
     233           1 :     if (*str == 'z') {
     234             :         // Must be "zz"
     235           0 :         str++;
     236           0 :         if (*str == 'z') { return NAG_BlackZugZwang; }     // zz     $23
     237           0 :         return 0;
     238             :     }
     239           1 :     if (*str == 'Z') {
     240             :         // Must be "ZZ"
     241           0 :         str++;
     242           0 :         if (*str == 'Z') { return NAG_ZugZwang; }          // ZZ     $22
     243           0 :         return 0;
     244             :     }
     245             : 
     246           1 :     if (*str == 'B') {
     247             :         // Must be "BB", "Bb"
     248           0 :         str++;
     249           0 :         if (*str == 'B') { return NAG_BishopPair; }        // BB     $151
     250           0 :         if (*str == 'b') { return NAG_OppositeBishops; }   // Bb     $153
     251           0 :         return 0;
     252             :     }
     253             : 
     254           1 :     if (*str == 'o') {
     255             :         // Must be "BB", "Bb"
     256           0 :         str++;
     257           0 :         if (*str == '-'  &&  str[1] == 'o') {              // o-o    $192
     258           0 :            return NAG_SeparatedPawns; }
     259           0 :         if (*str == 'o'  &&  str[1] == 0)   {              // [+]    $193
     260           0 :            return NAG_UnitedPawns; }
     261           0 :         if (*str == '^'  &&  str[1] == 0)   {              // o^     $212
     262           0 :            return NAG_PassedPawn; }
     263           0 :         return 0;
     264             :     }
     265             : 
     266           1 :     if (*str == '(') {
     267             :         // Must be (_)
     268           0 :         str++;
     269           0 :         if (*str == '_'  &&  str[1] == ')') {             // (_)     $142
     270           0 :            return NAG_BetterIs; }
     271           0 :         return 0;
     272             :     }
     273             : 
     274           1 :     if (*str == '[') {
     275             :         // Must be (_)
     276           0 :         str++;
     277           0 :         if (*str == ']'  &&  str[1] == 0) {                // []     $8
     278           0 :            return NAG_OnlyMove; }
     279           0 :         if (*str == '+'  &&  str[1] == ']') {              // [+]    $48
     280           0 :            return NAG_SlightCentre; }
     281           0 :         if (*str   == '+' &&
     282           0 :             str[1] == '+' && str[2] == ']') {              // [++]   $50
     283           0 :            return NAG_Centre; }
     284           0 :         return 0;
     285             :     }
     286             : 
     287           1 :     if (*str == '_') {
     288             :         // must be _|_ or _|
     289           0 :         str++;
     290           0 :         if (*str == '|'  &&  str[1] == '_') {              // _|_    $148
     291           0 :            return NAG_Ending; }
     292           0 :         if (*str == '|'  &&  str[1] == 0) {                // _|     $215
     293           0 :            return NAG_Without; }
     294           0 :         return 0;
     295             :     }
     296             : 
     297           1 :     if (*str == '|') {
     298             :         // must be ||, |_
     299           0 :         str++;
     300           0 :         if (*str == '|' ) { return NAG_Etc; }             // ||      $190
     301           0 :         if (*str == '_') { return NAG_With; }             // |_      $214
     302           0 :         return 0;
     303             :     }
     304             : 
     305           1 :     if (*str == '>') {
     306             :         // must be >, >>, >>>
     307           0 :         str++;
     308           0 :         if (*str == 0) { return NAG_SlightKingSide; }     // >       $54
     309           0 :         if (*str == '>'  &&  str[1] == 0) {               // >>      $56
     310           0 :            return NAG_ModerateKingSide; }
     311           0 :         if (*str == '>'  &&  str[1] == '>') {             // >>>     $58
     312           0 :            return NAG_KingSide; }
     313           0 :         return 0;
     314             :     }
     315             : 
     316           1 :     if (*str == '<') {
     317             :         // must be <, <<, <<<, <=>
     318           0 :         str++;
     319           0 :         if (*str == 0) { return NAG_SlightQueenSide; }   // <        $60
     320           0 :         if (*str == '<'  &&  str[1] == 0) {              // <<       $62
     321           0 :            return NAG_ModerateQueenSide; }
     322           0 :         if (*str   == '<'  &&                            // <<<      $64
     323           0 :             str[1] == '<' && str[2] == 0) { return NAG_QueenSide; }
     324           0 :         if (*str == '='  &&                              // <=>      $149
     325           0 :             str[1] == '>' && str[2] == 0) { return NAG_File; }
     326           0 :         if (*str == '+' &&                               // <+>      $130
     327           0 :             str[1] == '>' && str[2] == 0) { return NAG_SlightCounterPlay; }
     328           0 :         if (*str == '-' &&                               // <->      $131
     329           0 :               str[1] == '>' && str[2] == 0) { return NAG_BlackSlightCounterPlay; }
     330           0 :         if (*str == '+' &&                               // <++>     $132
     331           0 :               str[1] == '+' && str[2] == '>' && str[3] == 0) { return NAG_CounterPlay; }
     332           0 :         if (*str == '-' &&                               // <-->     $133
     333           0 :               str[1] == '-' && str[2] == '>' && str[3] == 0) { return NAG_BlackCounterPlay; }
     334           0 :         if (*str == '+' &&                               // <+++>    $134
     335           0 :               str[1] == '+' && str[2] == '+' && str[3] == '>') { return NAG_DecisiveCounterPlay; }
     336           0 :         if (*str   == '-' &&                             // <--->    $135
     337           0 :             str[1] == '-' && str[2] == '-' && str[3] == '>') { return NAG_BlackDecisiveCounterPlay; }
     338           0 :         return 0;
     339             :     }
     340             : 
     341           1 :     if (*str == '~' && *(str+1) == '=') {                // ~=       $44
     342             :        // alternative Compensation symbol:
     343           0 :         return NAG_Compensation;
     344             :     }
     345             : 
     346           1 :     if (*str == '~') {                                   // ~        $13
     347             :        // Unclear symbol:
     348           0 :         return NAG_Unclear;
     349             :     }
     350             : 
     351           1 :     if (*str == 'x') {                                   // x        $147
     352           0 :         return NAG_WeakPoint;
     353             :     }
     354             : 
     355           1 :     if (str[0] == 'N'  &&  str[1] == 0) {                // N        $146
     356             :        // Novelty symbol:
     357           0 :         return NAG_Novelty;
     358             :     }
     359             : 
     360           1 :     if (str[0] == 'D'  &&  str[1] == 0) {                // D        $201
     361             :        // Diagram symbol:
     362           0 :         return NAG_Diagram;
     363             :     }
     364           1 :     return 0;
     365             : }
     366             : 
     367         824 : errorT Game::AddNag (byte nag) {
     368         824 :     moveT * m = CurrentMove->prev;
     369         824 :     if (m->nagCount + 1 >= MAX_NAGS) { return ERROR_GameFull; }
     370         824 :     if (nag == 0) { /* Nags cannot be zero! */ return OK; }
     371             :         // If it is a move nag replace an existing
     372         824 :         if( nag >= 1 && nag <= 6)
     373         626 :                 for( int i=0; i<m->nagCount; i++)
     374           0 :                         if( m->nags[i] >= 1 && m->nags[i] <= 6)
     375             :                         {
     376           0 :                                 m->nags[i] = nag;
     377           0 :                                 return OK;
     378             :                         }
     379             :         // If it is a position nag replace an existing
     380         824 :         if( nag >= 10 && nag <= 21)
     381         208 :                 for( int i=0; i<m->nagCount; i++)
     382          10 :                         if( m->nags[i] >= 10 && m->nags[i] <= 21)
     383             :                         {
     384           0 :                                 m->nags[i] = nag;
     385           0 :                                 return OK;
     386             :                         }
     387         824 :         if( nag >= 1 && nag <= 6)
     388             :         {
     389             :                 // Put Move Nags at the beginning
     390         626 :                 for( int i=m->nagCount; i>0; i--)  m->nags[i] =  m->nags[i-1];
     391         626 :                 m->nags[0] = nag;
     392             :         }
     393             :         else
     394         198 :                 m->nags[m->nagCount] = nag;
     395         824 :         m->nagCount += 1;
     396         824 :         m->nags[m->nagCount] = 0;
     397         824 :     return OK;
     398             : }
     399             : 
     400           0 : errorT Game::RemoveNag (bool isMoveNag) {
     401           0 :     moveT * m = CurrentMove->prev;
     402           0 :         if( isMoveNag)
     403             :         {
     404           0 :                 for( int i=0; i<m->nagCount; i++)
     405           0 :                         if( m->nags[i] >= 1 && m->nags[i] <= 6)
     406             :                         {
     407           0 :                                 m->nagCount -= 1;
     408           0 :                                 for( int j=i; j<m->nagCount; j++)  m->nags[j] =  m->nags[j+1];
     409           0 :                                 m->nags[m->nagCount] = 0;
     410           0 :                                 return OK;
     411             :                         }
     412             :         }
     413             :         else
     414             :         {
     415           0 :                 for( int i=0; i<m->nagCount; i++)
     416           0 :                         if( m->nags[i] >= 10 && m->nags[i] <= 21)
     417             :                         {
     418           0 :                                 m->nagCount -= 1;
     419           0 :                                 for( int j=i; j<m->nagCount; j++)  m->nags[j] =  m->nags[j+1];
     420           0 :                                 m->nags[m->nagCount] = 0;
     421           0 :                                 return OK;
     422             :                         }
     423             :         }
     424           0 :     return OK;
     425             : }
     426             : 
     427             : //////////////////////////////////////////////////////////////////////
     428             : //  PUBLIC FUNCTIONS
     429             : //////////////////////////////////////////////////////////////////////
     430             : 
     431             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     432             : // Move allocation:
     433             : //      moves are allocated in chunks to save memory and for faster
     434             : //      performance.
     435             : //
     436             : constexpr int MOVE_CHUNKSIZE = 128;
     437             : 
     438     4375098 : moveT* Game::allocMove() {
     439     4375098 :         if (moveChunkUsed_ == MOVE_CHUNKSIZE) {
     440       34276 :                 moveChunks_.emplace_front(new moveT[MOVE_CHUNKSIZE]);
     441       34276 :                 moveChunkUsed_ = 0;
     442             :         }
     443     4375098 :         return moveChunks_.front().get() + moveChunkUsed_++;
     444             : }
     445             : 
     446     4373367 : moveT* Game::NewMove(markerT marker) {
     447     4373367 :         moveT* res = allocMove();
     448     4373367 :         res->clear();
     449     4373367 :         res->marker = marker;
     450     4373367 :         return res;
     451             : }
     452             : 
     453           6 : Game::Game(const Game& obj) {
     454           3 :         extraTags_ = obj.extraTags_;
     455           3 :         WhiteStr = obj.WhiteStr;
     456           3 :         BlackStr = obj.BlackStr;
     457           3 :         EventStr = obj.EventStr;
     458           3 :         SiteStr = obj.SiteStr;
     459           3 :         RoundStr = obj.RoundStr;
     460           3 :         Date = obj.Date;
     461           3 :         EventDate = obj.EventDate;
     462           3 :         EcoCode = obj.EcoCode;
     463           3 :         WhiteElo = obj.WhiteElo;
     464           3 :         BlackElo = obj.BlackElo;
     465           3 :         WhiteRatingType = obj.WhiteRatingType;
     466           3 :         BlackRatingType = obj.BlackRatingType;
     467           3 :         Result = obj.Result;
     468           3 :         std::copy_n(obj.ScidFlags, sizeof(obj.ScidFlags), ScidFlags);
     469             : 
     470           3 :         if (obj.StartPos)
     471           0 :                 StartPos = std::make_unique<Position>(*obj.StartPos);
     472             : 
     473           3 :         NumHalfMoves = obj.NumHalfMoves;
     474           3 :         PromotionsFlag = obj.PromotionsFlag;
     475           3 :         KeepDecodedMoves = obj.KeepDecodedMoves;
     476           3 :         WhiteEstimateElo = obj.WhiteEstimateElo;
     477           3 :         BlackEstimateElo = obj.BlackEstimateElo;
     478           3 :         NumMovesPrinted = obj.NumMovesPrinted;
     479           3 :         PgnStyle = obj.PgnStyle;
     480           3 :         PgnFormat = obj.PgnFormat;
     481           3 :         HtmlStyle = obj.HtmlStyle;
     482             : 
     483           3 :         moveChunkUsed_ = MOVE_CHUNKSIZE;
     484           3 :         FirstMove = obj.FirstMove->cloneLine(nullptr,
     485        1731 :                                              [this]() { return allocMove(); });
     486             : 
     487           3 :         MoveToLocationInPGN(obj.GetLocationInPGN());
     488           3 : }
     489           3 : Game* Game::clone() { return new Game(*this); }
     490             : 
     491           0 : void Game::strip(bool variations, bool comments, bool NAGs) {
     492           0 :     while (variations && MoveExitVariation() == OK) { // Go to main line
     493             :     }
     494             : 
     495           0 :     for (auto& chunk : moveChunks_) {
     496           0 :         moveT* move = chunk.get();
     497           0 :         moveT* end = (chunk == moveChunks_.front()) ? move + moveChunkUsed_
     498           0 :                                                     : move + MOVE_CHUNKSIZE;
     499           0 :         for (; move != end; ++move) {
     500           0 :             if (variations) {
     501           0 :                 move->numVariations = 0;
     502           0 :                 move->varChild = nullptr;
     503             :             }
     504           0 :             if (comments)
     505           0 :                 move->comment.clear();
     506             : 
     507           0 :             if (NAGs) {
     508           0 :                 move->nagCount = 0;
     509           0 :                 std::fill_n(move->nags, sizeof(move->nags), 0);
     510             :             }
     511             :         }
     512             :     }
     513           0 : }
     514             : 
     515             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     516             : // Game::ClearMoves(): clear all moves.
     517        5209 : void Game::ClearMoves() {
     518             :     // Delete any chunks of moves except the first:
     519        5209 :     if (moveChunks_.empty()) {
     520        2139 :         moveChunkUsed_ = MOVE_CHUNKSIZE;
     521             :     } else {
     522        3070 :         moveChunks_.erase_after(moveChunks_.begin(), moveChunks_.end());
     523        3070 :         moveChunkUsed_ = 0;
     524             :     }
     525        5209 :     StartPos = nullptr;
     526        5209 :     CurrentPos->StdStart();
     527             : 
     528             :     // Initialize FirstMove: start and end of movelist markers
     529        5209 :     FirstMove = NewMove(START_MARKER);
     530        5209 :     CurrentMove = NewMove(END_MARKER);
     531        5209 :     FirstMove->setNext(CurrentMove);
     532             : 
     533        5209 :     VarDepth = 0;
     534        5209 :     NumHalfMoves = 0;
     535        5209 : }
     536             : 
     537             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     538             : // Game::Clear():
     539             : //      Reset the game to its normal empty state.
     540             : //
     541        5204 : void Game::Clear() {
     542        5204 :         extraTags_.clear();
     543        5204 :         WhiteStr.clear();
     544        5204 :         BlackStr.clear();
     545        5204 :         EventStr.clear();
     546        5204 :         SiteStr.clear();
     547        5204 :         RoundStr.clear();
     548        5204 :         Date = ZERO_DATE;
     549        5204 :         EventDate = ZERO_DATE;
     550        5204 :         EcoCode = 0;
     551        5204 :         WhiteElo = BlackElo = 0;
     552        5204 :         WhiteEstimateElo = BlackEstimateElo = 0;
     553        5204 :         WhiteRatingType = BlackRatingType = RATING_Elo;
     554        5204 :         Result = RESULT_None;
     555        5204 :         ScidFlags[0] = 0;
     556             : 
     557        5204 :         NumMovesPrinted = 0;
     558        5204 :         PgnStyle = PGN_STYLE_TAGS | PGN_STYLE_VARS | PGN_STYLE_COMMENTS;
     559        5204 :         PgnFormat = PGN_FORMAT_Plain;
     560        5204 :         HtmlStyle = 0;
     561             : 
     562        5204 :         ClearMoves();
     563        5204 :         KeepDecodedMoves = true;
     564        5204 :         PromotionsFlag = false;
     565        5204 : }
     566             : 
     567             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     568             : // Game::PgnFormatFromString():
     569             : //      Converts a string to a gameFormatT, returning true on success
     570             : //      or false on error.
     571             : //      The string should be a case-insensitive unique prefix of
     572             : //      "plain" (or "pgn"), "HTML", "LaTeX" or "Color".
     573             : bool
     574           0 : Game::PgnFormatFromString (const char * str, gameFormatT * fmt)
     575             : {
     576           0 :     if (strIsCasePrefix (str, "Plain")) {
     577           0 :         *fmt = PGN_FORMAT_Plain;
     578           0 :     } else if (strIsCasePrefix (str, "PGN")) {
     579           0 :         *fmt = PGN_FORMAT_Plain;
     580           0 :     } else if (strIsCasePrefix (str, "HTML")) {
     581           0 :         *fmt = PGN_FORMAT_HTML;
     582           0 :     } else if (strIsCasePrefix (str, "LaTeX")) {
     583           0 :         *fmt = PGN_FORMAT_LaTeX;
     584           0 :     } else if (strIsCasePrefix (str, "Color")) {
     585           0 :         *fmt = PGN_FORMAT_Color;
     586             :     } else {
     587           0 :         return false;
     588             :     }
     589           0 :     return true;
     590             : }
     591             : 
     592             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     593             : // Game::SetPgnFormatFromString():
     594             : //      Sets the PgnFormat from the provided string.
     595             : //      Returns true if the PgnFormat was successfully set.
     596             : bool
     597           0 : Game::SetPgnFormatFromString (const char * str)
     598             : {
     599           0 :     return PgnFormatFromString (str, &PgnFormat);
     600             : }
     601             : 
     602             : errorT
     603           5 : Game::SetStartFen (const char * fenStr)
     604             : {
     605          10 :     auto pos = std::make_unique<Position>();
     606           5 :     errorT err = pos->ReadFromFEN (fenStr);
     607           5 :     if (err != OK)
     608           0 :         return err;
     609             : 
     610           5 :     ClearMoves();
     611           5 :     StartPos = std::move(pos);
     612           5 :     *CurrentPos = *StartPos;
     613           5 :     return OK;
     614             : }
     615             : 
     616             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     617             : // Game::AddPgnTag(): Add a PGN Tag.
     618             : //
     619          15 : void Game::AddPgnTag(const char* tag, const char* value) {
     620             :     // First, try to replace an existing tag:
     621          24 :     for (auto& e : extraTags_) {
     622           9 :         if (e.first == tag) {
     623           0 :             e.second.assign(value);
     624           0 :             return;
     625             :         }
     626             :     }
     627          15 :     extraTags_.emplace_back(tag, value);
     628             : }
     629             : 
     630             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     631             : // Game::FindExtraTag():
     632             : //   Finds and returns an extra PGN tag if it
     633             : //   exists, or NULL if it does not exist.
     634           0 : const char* Game::FindExtraTag(const char* tag) const {
     635           0 :     for (auto& e : extraTags_) {
     636           0 :         if (e.first == tag)
     637           0 :             return e.second.c_str();
     638             :     }
     639           0 :     return NULL;
     640             : }
     641             : 
     642             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     643             : // Game::RemoveExtraTag():
     644             : //   Remove an extra PGN tag if it exists.
     645           0 : bool Game::RemoveExtraTag(const char* tag) {
     646             :     auto it = std::remove_if(extraTags_.begin(), extraTags_.end(),
     647           0 :                              [&](const std::pair<std::string, std::string>& e) {
     648           0 :                                  return e.first == tag;
     649           0 :                              });
     650           0 :     if (it != extraTags_.end()) {
     651           0 :         extraTags_.erase(it, extraTags_.end());
     652           0 :         return true;
     653             :     }
     654           0 :     return false;
     655             : }
     656             : 
     657       10921 : std::string& Game::accessTagValue(const char* tag, size_t tagLen) {
     658       10921 :         if (tagLen == 5) {
     659        8213 :                 if (std::equal(tag, tag + 5, "Event"))
     660        2040 :                         return EventStr;
     661        6173 :                 if (std::equal(tag, tag + 5, "Round"))
     662        2039 :                         return RoundStr;
     663        4134 :                 if (std::equal(tag, tag + 5, "White"))
     664        2039 :                         return WhiteStr;
     665        2095 :                 if (std::equal(tag, tag + 5, "Black"))
     666        2039 :                         return BlackStr;
     667        2708 :         } else if (tagLen == 4) {
     668        2040 :                 if (std::equal(tag, tag + 4, "Site"))
     669        2040 :                         return SiteStr;
     670             :         }
     671             : 
     672        9332 :         for (auto& elem : extraTags_) {
     673        8608 :                 if (std::equal(tag, tag + tagLen, elem.first.begin(), elem.first.end()))
     674           0 :                         return elem.second;
     675             :         }
     676         724 :         extraTags_.emplace_back();
     677         724 :         extraTags_.back().first.assign(tag, tagLen);
     678         724 :         return extraTags_.back().second;
     679             : }
     680             : 
     681             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     682             : // Game::SetMoveComment():
     683             : //      Sets the comment for a move. A comment before the game itself
     684             : //      is stored as a comment of FirstMove.
     685             : //
     686             : void
     687      111904 : Game::SetMoveComment (const char * comment)
     688             : {
     689      111904 :     ASSERT (CurrentMove != NULL  &&  CurrentMove->prev != NULL);
     690      111904 :     moveT * m = CurrentMove->prev;
     691      111904 :     if (comment == NULL) {
     692           0 :         m->comment.clear();
     693             :     } else {
     694      111904 :         m->comment = comment;
     695             :         // CommentsFlag = 1;
     696             :     }
     697      111904 : }
     698             : 
     699          73 : int Game::setRating(colorT col, const char* ratingType, size_t ratingTypeLen,
     700             :                     std::pair<const char*, const char*> rating) {
     701          73 :         auto begin = ratingTypeNames;
     702          73 :         const size_t ratingSz = 7;
     703         148 :         auto it = std::find_if(begin, begin + ratingSz, [&](auto rType) {
     704          75 :                 return std::equal(ratingType, ratingType + ratingTypeLen, rType,
     705          75 :                                   rType + std::strlen(rType));
     706         148 :         });
     707          73 :         byte rType = static_cast<byte>(std::distance(begin, it));
     708          73 :         if (rType >= ratingSz)
     709           0 :                 return -1;
     710             : 
     711          73 :         int res = 1;
     712          73 :         auto elo = strGetUnsigned(std::string{rating.first, rating.second}.c_str());
     713          73 :         if (elo > MAX_ELO) {
     714           0 :                 elo = 0;
     715           0 :                 res = 0;
     716             :         }
     717          73 :         if (col == WHITE) {
     718          37 :                 SetWhiteElo(static_cast<eloT>(elo));
     719          37 :                 SetWhiteRatingType(rType);
     720             :         } else {
     721          36 :                 SetBlackElo(static_cast<eloT>(elo));
     722          36 :                 SetBlackRatingType(rType);
     723             :         }
     724          73 :         return res;
     725             : }
     726             : 
     727             : ///////////////////////////////////////////////////////////////////////////
     728             : // A "location" in the game is represented by a position (Game::CurrentPos), the
     729             : // next move to be played (Game::CurrentMove) and the number of parent variations
     730             : // (Game::VarDepth). Since CurrentMove is the next move to be played, some
     731             : // invariants must hold: it is never nullptr and it never points to a
     732             : // START_MARKER (it will point to a END_MARKER if there are no more moves). This
     733             : // also means that CurrentMove->prev is always valid: it will point to a
     734             : // previous move or to a START_MARKER.
     735             : // The following functions modify ONLY the current location of the game.
     736             : 
     737             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     738             : // Move current position forward one move.
     739             : // Also update all the necessary fields in the simpleMove structure
     740             : // (CurrentMove->moveData) so it can be undone.
     741             : //
     742     8579393 : errorT Game::MoveForward(void) {
     743     8579393 :         if (CurrentMove->endMarker())
     744       49453 :                 return ERROR_EndOfMoveList;
     745             : 
     746     8529386 :         CurrentPos->DoSimpleMove(&CurrentMove->moveData);
     747     8529940 :         CurrentMove = CurrentMove->next;
     748             : 
     749             :         // Invariants
     750     8529940 :         ASSERT(CurrentMove && CurrentMove->prev);
     751     8529940 :         ASSERT(!CurrentMove->startMarker());
     752     8529940 :         return OK;
     753             : }
     754             : 
     755             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     756             : // Game::MoveBackup():
     757             : //      Backup one move.
     758             : //
     759     5150374 : errorT Game::MoveBackup(void) {
     760     5150374 :         if (CurrentMove->prev->startMarker())
     761      970562 :                 return ERROR_StartOfMoveList;
     762             : 
     763     4179812 :         CurrentMove = CurrentMove->prev;
     764     4179812 :         CurrentPos->UndoSimpleMove(&CurrentMove->moveData);
     765             : 
     766             :         // Invariants
     767     4179812 :         ASSERT(CurrentMove && CurrentMove->prev);
     768     4179812 :         ASSERT(!CurrentMove->startMarker());
     769     4179812 :         return OK;
     770             : }
     771             : 
     772             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     773             : // Game::MoveIntoVariation():
     774             : //      Move into a subvariation. Variations are numbered from 0.
     775      382822 : errorT Game::MoveIntoVariation(uint varNumber) {
     776      429336 :         for (auto subVar = CurrentMove; subVar->varChild; --varNumber) {
     777      395960 :                 subVar = subVar->varChild;
     778      395960 :                 if (varNumber == 0) {
     779      349446 :                         CurrentMove = subVar->next; // skip the START_MARKER
     780      349446 :                         ++VarDepth;
     781             : 
     782             :                         // Invariants
     783      349446 :                         ASSERT(CurrentMove && CurrentMove->prev);
     784      349446 :                         ASSERT(!CurrentMove->startMarker());
     785      349446 :                         return OK;
     786             :                 }
     787             :         }
     788       33376 :         return ERROR_NoVariation; // there is no such variation
     789             : }
     790             : 
     791             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     792             : // Game::MoveExitVariation():
     793             : //      Move out of a variation, to the parent.
     794             : //
     795      964410 : errorT Game::MoveExitVariation(void) {
     796      964410 :         if (VarDepth == 0) // not in a variation!
     797           0 :                 return ERROR_NoVariation;
     798             : 
     799             :         // Algorithm: go back previous moves as far as possible, then
     800             :         // go up to the parent of the variation.
     801     4490547 :         while (MoveBackup() == OK) {
     802             :         }
     803      964410 :         CurrentMove = CurrentMove->getParent().first;
     804      964410 :         --VarDepth;
     805             : 
     806             :         // Invariants
     807      964410 :         ASSERT(CurrentMove && CurrentMove->prev);
     808      964410 :         ASSERT(!CurrentMove->startMarker());
     809      964410 :         return OK;
     810             : }
     811             : 
     812             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     813             : // Move to the beginning of the game.
     814             : //
     815       13660 : void Game::MoveToStart() {
     816       13660 :         if (StartPos) {
     817           0 :                 *CurrentPos = *StartPos;
     818             :         } else {
     819       13660 :                 CurrentPos->StdStart();
     820             :         }
     821       13660 :         VarDepth = 0;
     822       13660 :         CurrentMove = FirstMove->next;
     823             : 
     824             :         // Invariants
     825       13660 :         ASSERT(CurrentMove && CurrentMove->prev);
     826       13660 :         ASSERT(!CurrentMove->startMarker());
     827       13660 : }
     828             : 
     829      423596 : errorT Game::MoveForwardInPGN() {
     830      423596 :         if (CurrentMove->prev->varChild && MoveBackup() == OK)
     831       34927 :                 return MoveIntoVariation(0);
     832             : 
     833       33376 :         while (MoveForward() != OK) {
     834       39435 :                 if (VarDepth == 0)
     835          18 :                         return ERROR_EndOfMoveList;
     836             : 
     837       39417 :                 auto varnum = GetVarNumber();
     838       39417 :                 MoveExitVariation();
     839       39417 :                 if (MoveIntoVariation(varnum + 1) == OK)
     840        6041 :                         return OK;
     841             : 
     842       33376 :                 MoveForward();
     843             :         }
     844      382610 :         return OK;
     845             : }
     846             : 
     847        1590 : errorT Game::MoveToLocationInPGN(unsigned stopLocation) {
     848        1590 :         MoveToPly(0);
     849      422329 :         for (unsigned loc = 1; loc < stopLocation; ++loc) {
     850      420742 :                 errorT err = MoveForwardInPGN();
     851      420742 :                 if (err != OK)
     852           3 :                         return err;
     853             :         }
     854        1587 :         return OK;
     855             : }
     856             : 
     857        3165 : unsigned Game::GetLocationInPGN() const {
     858        3165 :         unsigned res = 1;
     859        3165 :         const moveT* last_move = CurrentMove->prev;
     860        3165 :         const moveT* move = FirstMove;
     861     1829909 :         for (; move != last_move; move = move->nextMoveInPGN()) {
     862      913372 :                 if (!move->endMarker())
     863      835655 :                         ++res;
     864             :         }
     865        3165 :         return res;
     866             : }
     867             : 
     868        1443 : unsigned Game::GetPgnOffset() const {
     869        1443 :         unsigned res = 1;
     870        1443 :         const moveT* last_move = CurrentMove->getPrevMove();
     871        1443 :         if (last_move) {
     872        1443 :                 const moveT* move = FirstMove;
     873      839719 :                 for (; move != last_move; move = move->nextMoveInPGN()) {
     874      419138 :                         if (!move->endMarker())
     875      383488 :                                 ++res;
     876             :                 }
     877             :         }
     878        1443 :         return res;
     879             : }
     880             : 
     881             : ///////////////////////////////////////////////////////////////////////////
     882             : // The following functions modify the moves graph in order to add or delete
     883             : // moves. Promoting variations also modifies the moves graph.
     884             : 
     885             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     886             : // Game::AddMove():
     887             : //      Add a move at current position and do it.
     888             : //
     889     3127677 : errorT Game::AddMove(const simpleMoveT* sm) {
     890     3127677 :         ASSERT(sm != NULL);
     891             : 
     892             :         // We must be at the end of a game/variation to add a move:
     893     3127677 :         if (!CurrentMove->endMarker())
     894           0 :                 Truncate();
     895             : 
     896     3127677 :         CurrentMove->setNext(NewMove(END_MARKER));
     897     3127677 :         CurrentMove->marker = NO_MARKER;
     898     3127677 :         CurrentMove->moveData.from = sm->from;
     899     3127677 :         CurrentMove->moveData.to = sm->to;
     900     3127677 :         CurrentMove->moveData.promote = sm->promote;
     901     3127677 :         CurrentMove->moveData.movingPiece = sm->movingPiece;
     902     3127677 :         if (VarDepth == 0)
     903      941265 :                 ++NumHalfMoves;
     904             : 
     905     3127677 :         return MoveForward();
     906             : }
     907             : 
     908             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     909             : // Game::AddVariation():
     910             : //      Add a variation for the current move.
     911             : //      Also moves into the variation.
     912      617636 : errorT Game::AddVariation() {
     913      617636 :         auto err = MoveBackup();
     914      617636 :         if (err != OK)
     915           0 :                 return err;
     916             : 
     917      617636 :         auto newVar = NewMove(START_MARKER);
     918      617636 :         newVar->setNext(NewMove(END_MARKER));
     919      617636 :         CurrentMove->appendChild(newVar);
     920             : 
     921             :         // Move into variation
     922      617636 :         CurrentMove = newVar->next;
     923      617636 :         ++VarDepth;
     924             : 
     925             :         // Invariants
     926      617636 :         ASSERT(CurrentMove && CurrentMove->prev);
     927      617636 :         ASSERT(!CurrentMove->startMarker());
     928      617636 :         return OK;
     929             : }
     930             : 
     931             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     932             : // Game::FirstVariation():
     933             : // Promotes the current variation to first variation.
     934           0 : errorT Game::FirstVariation() {
     935           0 :         auto parent = CurrentMove->getParent();
     936           0 :         auto root = parent.first;
     937           0 :         if (!root)
     938           0 :                 return ERROR_NoVariation;
     939             : 
     940           0 :         root->detachChild(parent.second);
     941           0 :         root->insertChild(parent.second, 0);
     942           0 :         return OK;
     943             : }
     944             : 
     945             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     946             : // Game::MainVariation():
     947             : //    Like FirstVariation, but promotes the variation to the main line,
     948             : //    demoting the main line to be the first variation.
     949           0 : errorT Game::MainVariation() {
     950           0 :         auto parent = CurrentMove->getParent();
     951           0 :         auto root = parent.first;
     952           0 :         if (!root)
     953           0 :                 return ERROR_NoVariation;
     954           0 :         if (parent.second->next->endMarker()) // Do not promote empty variations
     955           0 :                 return OK;
     956             : 
     957             :         // Make the current variation the first variation
     958           0 :         root->detachChild(parent.second);
     959           0 :         root->insertChild(parent.second, 0);
     960             : 
     961             :         // Swap the mainline with the current variation
     962           0 :         root->swapLine(*parent.second->next);
     963             : 
     964           0 :         ASSERT(VarDepth);
     965           0 :         --VarDepth;
     966             : 
     967             :         // Now, the information about the material at the end of the
     968             :         // game, pawn promotions, will be wrong if the variation was
     969             :         // promoted to an actual game move, so call MakeHomePawnList()
     970             :         // to go through the game moves and ensure it is correct.
     971           0 :         auto location = currentLocation();
     972             :         byte tempPawnList[9];
     973           0 :         MakeHomePawnList(tempPawnList);
     974           0 :         restoreLocation(location);
     975             : 
     976           0 :         return OK;
     977             : }
     978             : 
     979             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     980             : // Game::DeleteVariation():
     981             : //      Deletes a variation. Variations are numbered from 0.
     982             : //      Note that for speed and simplicity, freed moves are not
     983             : //      added to the free list. This means that repeatedly adding and
     984             : //      deleting variations will waste memory until the game is cleared.
     985             : //
     986           0 : errorT Game::DeleteVariation() {
     987           0 :         auto parent = CurrentMove->getParent();
     988           0 :         auto root = parent.first;
     989           0 :         if (!root || MoveExitVariation() != OK)
     990           0 :                 return ERROR_NoVariation;
     991             : 
     992           0 :         root->detachChild(parent.second);
     993           0 :         return OK;
     994             : }
     995             : 
     996             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     997             : // Game::Truncate():
     998             : //      Truncate game at the current move.
     999             : //      For speed and simplicity, moves and comments are not freed.
    1000             : //      So repeatedly adding moves and truncating a game will waste
    1001             : //      memory until the game is cleared.
    1002           0 : void Game::Truncate() {
    1003           0 :         if (CurrentMove->endMarker())
    1004           0 :                 return;
    1005             : 
    1006           0 :         auto endMove = NewMove(END_MARKER);
    1007           0 :         CurrentMove->prev->setNext(endMove);
    1008             : 
    1009           0 :         CurrentMove = endMove;
    1010           0 :         if (VarDepth == 0)
    1011           0 :                 NumHalfMoves = GetCurrentPly();
    1012             : 
    1013             :         // Invariants
    1014           0 :         ASSERT(CurrentMove && CurrentMove->prev);
    1015           0 :         ASSERT(!CurrentMove->startMarker());
    1016             : }
    1017             : 
    1018             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1019             : // Game::TruncateStart():
    1020             : //      Truncate all moves leading to current position.
    1021           0 : void Game::TruncateStart() {
    1022             :     // It is necessary to rebuild the current position using ReadFromFEN()
    1023             :     // because the order of pieces is important when encoding to SCIDv4 format.
    1024             :     char tempStr[256];
    1025           0 :     CurrentPos->PrintFEN(tempStr, FEN_ALL_FIELDS);
    1026           0 :     auto pos = std::make_unique<Position>();
    1027           0 :     if (pos->ReadFromFEN(tempStr) != OK)
    1028           0 :         return;
    1029             : 
    1030           0 :     if (VarDepth != 0 && MainVariation() != OK)
    1031           0 :                 return;
    1032             : 
    1033           0 :     NumHalfMoves -= GetCurrentPly();
    1034           0 :     StartPos = std::move(pos);
    1035           0 :     *CurrentPos = *StartPos;
    1036           0 :         FirstMove->setNext(CurrentMove);
    1037             : }
    1038             : 
    1039             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1040             : // Game::MakeHomePawnList():
    1041             : //    Is passed an array of 9 bytes and fills it with the game's
    1042             : //    home pawn delta information.
    1043             : //    This function also ensures that other information about the
    1044             : //    game that will be stored in the index file and used to speed
    1045             : //    up searches (material at end of game, etc) is up to date.
    1046             : 
    1047       10018 : bool Game::MakeHomePawnList(byte* pbPawnList) {
    1048       10018 :     ASSERT(pbPawnList != nullptr);
    1049             :     // We zero out the list first:
    1050       10018 :     std::fill_n(pbPawnList, 9, 0);
    1051             : 
    1052       10018 :     uint count = 0;
    1053       10018 :     uint halfByte = 0;
    1054       10018 :     errorT err = OK;
    1055       10018 :     uint hpOld = HPSIG_StdStart;    // All 16 pawns are on their home squares.
    1056       10018 :     byte* pbList = pbPawnList +1;
    1057             : 
    1058       10018 :     NumHalfMoves = 0;
    1059       10018 :     PromotionsFlag = false;
    1060       10018 :     bool UnderPromosFlag = false;
    1061       10018 :     MoveToPly(0);
    1062             : 
    1063     5643410 :     while (err == OK) {
    1064     2816696 :         uint hpNew = CurrentPos->GetHPSig();
    1065     2816696 :         uint changed = hpOld - hpNew;
    1066     2816696 :         if (changed != 0 && !HasNonStandardStart()) {
    1067             :             // Find the idx of the moved pawn
    1068      149217 :             uint changeValue = 0;
    1069             :             while (1) {
    1070     2386485 :                 changed = changed >> 1;
    1071     1267851 :                 if (changed == 0) break;
    1072     1118634 :                 changeValue++;
    1073             :             }
    1074             : 
    1075             :             // There are only 16 pawns, so we can store two pawn moves
    1076             :             // in every byte
    1077      149217 :             if (halfByte == 0) {
    1078       75507 :                 *pbList = (changeValue << 4);  halfByte = 1;
    1079             :             } else {
    1080       73710 :                 *pbList |= (changeValue & 15);  pbList++;  halfByte = 0;
    1081             :             }
    1082      149217 :             hpOld = hpNew;
    1083      149217 :             count++;
    1084             :         }
    1085     2816696 :         if (CurrentMove->marker != END_MARKER) {
    1086     2806678 :             if (CurrentMove->moveData.promote != EMPTY) {
    1087       21976 :                 PromotionsFlag = true;
    1088       21976 :                 if (piece_Type(CurrentMove->moveData.promote) != QUEEN) {
    1089       18808 :                     UnderPromosFlag = true;
    1090             :                 }
    1091             :             }
    1092             :         }
    1093     2816696 :         err = MoveForward();
    1094     2816696 :         if (err == OK) { NumHalfMoves++; }
    1095             :     }
    1096             :     // First byte in pawnlist array stores the count:
    1097       10018 :     pbPawnList[0] = (byte) count;
    1098             : 
    1099       10018 :     return UnderPromosFlag;
    1100             : }
    1101             : 
    1102             : namespace {
    1103             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1104             : // calcHomePawnMask():
    1105             : //      Computes the homePawn mask for a position.
    1106             : //
    1107           0 : int calcHomePawnMask (pieceT pawn, const pieceT* board)
    1108             : {
    1109           0 :     ASSERT (pawn == WP  ||  pawn == BP);
    1110           0 :     const pieceT* bd = &(board[ (pawn == WP ? H2 : H7) ]);
    1111           0 :     int result = 0;
    1112           0 :     if (*bd == pawn) { result |= 128; }  bd--;   // H-fyle pawn
    1113           0 :     if (*bd == pawn) { result |=  64; }  bd--;   // G-fyle pawn
    1114           0 :     if (*bd == pawn) { result |=  32; }  bd--;   // F-fyle pawn
    1115           0 :     if (*bd == pawn) { result |=  16; }  bd--;   // E-fyle pawn
    1116           0 :     if (*bd == pawn) { result |=   8; }  bd--;   // D-fyle pawn
    1117           0 :     if (*bd == pawn) { result |=   4; }  bd--;   // C-fyle pawn
    1118           0 :     if (*bd == pawn) { result |=   2; }  bd--;   // B-fyle pawn
    1119           0 :     if (*bd == pawn) { result |=   1; }          // A-fyle pawn
    1120           0 :     return result;
    1121             : }
    1122             : 
    1123             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1124             : // patternsMatch():
    1125             : //      Used by Game::MaterialMatch() to test patterns.
    1126             : //      Returns 1 if all the patterns in the list match, 0 otherwise.
    1127             : //
    1128           0 : int patternsMatch(const Position* pos, patternT* ptn) {
    1129           0 :     const pieceT* board = pos->GetBoard();
    1130           0 :     while (ptn != NULL) {
    1131           0 :         if (ptn->rankMatch == NO_RANK) {
    1132             : 
    1133           0 :             if (ptn->fyleMatch == NO_FYLE) { // Nothing to test!
    1134             :             } else {  // Test this fyle:
    1135           0 :                 squareT sq = square_Make (ptn->fyleMatch, RANK_1);
    1136           0 :                 int found = 0;
    1137           0 :                 for (uint i=0; i < 8; i++, sq += 8) {
    1138           0 :                     if (board[sq] == ptn->pieceMatch) { found = 1; break; }
    1139             :                 }
    1140           0 :                 if (found != ptn->flag) { return 0; }
    1141             :             }
    1142             : 
    1143             :         } else { // rankMatch is a rank from 1 to 8:
    1144             : 
    1145           0 :             if (ptn->fyleMatch == NO_FYLE) { // Test the whole rank:
    1146           0 :                 int found = 0;
    1147           0 :                 squareT sq = square_Make (A_FYLE, ptn->rankMatch);
    1148           0 :                 for (uint i=0; i < 8; i++, sq++) {
    1149           0 :                     if (board[sq] == ptn->pieceMatch) { found = 1; break; }
    1150             :                 }
    1151           0 :                 if (found != ptn->flag) { return 0; }
    1152             :             } else {  // Just test one square:
    1153           0 :                 squareT sq = square_Make(ptn->fyleMatch, ptn->rankMatch);
    1154           0 :                 int found = 0;
    1155           0 :                 if (board[sq] == ptn->pieceMatch) { found = 1; }
    1156           0 :                 if (found != ptn->flag) { return 0; }
    1157             :             }
    1158             :         }
    1159             : 
    1160             :         // If we get this far, this pattern matched. Try the next one:
    1161           0 :         ptn = ptn->next;
    1162             :     }
    1163             : 
    1164             :     // If we reach here, all patterns matched:
    1165           0 :     return 1;
    1166             : }
    1167             : } // end of anonymous namespace
    1168             : 
    1169             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1170             : // Game::MaterialMatch(): Material search test.
    1171             : //      The parameters min and max should each be an array of 15
    1172             : //      counts, to specify the maximum and minimum number of counts
    1173             : //      of each type of piece.
    1174             : //
    1175             : bool
    1176           0 : Game::MaterialMatch (ByteBuffer * buf, byte * min, byte * max,
    1177             :                      patternT * patterns, int minPly, int maxPly,
    1178             :                      int matchLength, bool oppBishops, bool sameBishops,
    1179             :                      int minDiff, int maxDiff)
    1180             : {
    1181             :     // If buf is NULL, the game is in memory. Otherwise, Decode only
    1182             :     // the necessary moves:
    1183           0 :     errorT err = OK;
    1184             : 
    1185           0 :     if (buf == NULL) {
    1186           0 :         MoveToPly(0);
    1187             :     } else {
    1188           0 :         Clear();
    1189           0 :         err = DecodeStart (buf);
    1190           0 :         KeepDecodedMoves = false;
    1191             :     }
    1192             : 
    1193           0 :     ASSERT (matchLength >= 1);
    1194             : 
    1195           0 :     int matchesNeeded = matchLength;
    1196             :     int matDiff;
    1197           0 :     uint plyCount = 0;
    1198           0 :     while (err == OK) {
    1199           0 :         bool foundMatch = false;
    1200             :         byte wMinor, bMinor;
    1201             : 
    1202             :         // If current pos has LESS than the minimum of pawns, this
    1203             :         // game can never match so return false;
    1204           0 :         if (CurrentPos->PieceCount(WP) < min[WP]) { return false; }
    1205           0 :         if (CurrentPos->PieceCount(BP) < min[BP]) { return false; }
    1206             : 
    1207             :         // If not in the valid move range, go to the next move or return:
    1208           0 :         if ((int)plyCount > maxPly) { return false; }
    1209           0 :         if ((int)plyCount < minPly) { goto Next_Move; }
    1210             : 
    1211             : // For these comparisons, we really could only do half of them each move,
    1212             : // according to which side just moved.
    1213             :         // For non-pawns, the count could be increased by promotions:
    1214           0 :         if (CurrentPos->PieceCount(WQ) < min[WQ]) { goto Check_Promotions; }
    1215           0 :         if (CurrentPos->PieceCount(BQ) < min[BQ]) { goto Check_Promotions; }
    1216           0 :         if (CurrentPos->PieceCount(WR) < min[WR]) { goto Check_Promotions; }
    1217           0 :         if (CurrentPos->PieceCount(BR) < min[BR]) { goto Check_Promotions; }
    1218           0 :         if (CurrentPos->PieceCount(WB) < min[WB]) { goto Check_Promotions; }
    1219           0 :         if (CurrentPos->PieceCount(BB) < min[BB]) { goto Check_Promotions; }
    1220           0 :         if (CurrentPos->PieceCount(WN) < min[WN]) { goto Check_Promotions; }
    1221           0 :         if (CurrentPos->PieceCount(BN) < min[BN]) { goto Check_Promotions; }
    1222           0 :         wMinor = CurrentPos->PieceCount(WB) + CurrentPos->PieceCount(WN);
    1223           0 :         bMinor = CurrentPos->PieceCount(BB) + CurrentPos->PieceCount(BN);
    1224           0 :         if (wMinor < min[WM]) { goto Check_Promotions; }
    1225           0 :         if (bMinor < min[BM]) { goto Check_Promotions; }
    1226             : 
    1227             :         // Now test maximum counts:
    1228           0 :         if (CurrentPos->PieceCount(WQ) > max[WQ]) { goto Next_Move; }
    1229           0 :         if (CurrentPos->PieceCount(BQ) > max[BQ]) { goto Next_Move; }
    1230           0 :         if (CurrentPos->PieceCount(WR) > max[WR]) { goto Next_Move; }
    1231           0 :         if (CurrentPos->PieceCount(BR) > max[BR]) { goto Next_Move; }
    1232           0 :         if (CurrentPos->PieceCount(WB) > max[WB]) { goto Next_Move; }
    1233           0 :         if (CurrentPos->PieceCount(BB) > max[BB]) { goto Next_Move; }
    1234           0 :         if (CurrentPos->PieceCount(WN) > max[WN]) { goto Next_Move; }
    1235           0 :         if (CurrentPos->PieceCount(BN) > max[BN]) { goto Next_Move; }
    1236           0 :         if (CurrentPos->PieceCount(WP) > max[WP]) { goto Next_Move; }
    1237           0 :         if (CurrentPos->PieceCount(BP) > max[BP]) { goto Next_Move; }
    1238           0 :         if (wMinor > max[WM]) { goto Next_Move; }
    1239           0 :         if (bMinor > max[BM]) { goto Next_Move; }
    1240             : 
    1241             :         // If both sides have ONE bishop, we need to check if the search
    1242             :         // was restricted to same-color or opposite-color bishops:
    1243           0 :         if (CurrentPos->PieceCount(WB) == 1
    1244           0 :                 && CurrentPos->PieceCount(BB) == 1) {
    1245           0 :             if (!oppBishops  ||  !sameBishops) { // Check the restriction:
    1246           0 :                 colorT whiteBishCol = NOCOLOR;
    1247           0 :                 colorT blackBishCol = NOCOLOR;
    1248             : 
    1249             :                 // Search for the white and black bishop, to find their
    1250             :                 // square color:
    1251           0 :                 const pieceT* bd = CurrentPos->GetBoard();
    1252           0 :                 for (squareT sq = A1; sq <= H8; sq++) {
    1253           0 :                     if (bd[sq] == WB) {
    1254           0 :                         whiteBishCol = BOARD_SQUARECOLOR [sq];
    1255           0 :                     } else if (bd[sq] == BB) {
    1256           0 :                         blackBishCol = BOARD_SQUARECOLOR [sq];
    1257             :                     }
    1258             :                 }
    1259             :                 // They should be valid colors:
    1260           0 :                 ASSERT (blackBishCol != NOCOLOR  &&  whiteBishCol != NOCOLOR);
    1261             : 
    1262             :                 // If the square colors do not match the restriction,
    1263             :                 // then this game cannot match:
    1264           0 :                 if (oppBishops  &&  blackBishCol == whiteBishCol) {
    1265           0 :                     return false;
    1266             :                 }
    1267           0 :                 if (sameBishops  &&  blackBishCol != whiteBishCol) {
    1268           0 :                     return false;
    1269             :                 }
    1270             :             }
    1271             :         }
    1272             : 
    1273             :         // Now check if the material difference is in-range:
    1274           0 :         matDiff = (int)CurrentPos->MaterialValue(WHITE) -
    1275           0 :                   (int)CurrentPos->MaterialValue(BLACK);
    1276           0 :         if (matDiff < minDiff  ||  matDiff > maxDiff) { goto Next_Move; }
    1277             : 
    1278             :         // At this point, the Material matches; do the patterns match?
    1279           0 :         if (patterns == NULL || patternsMatch(currentPos(), patterns)) {
    1280           0 :             foundMatch = true;
    1281           0 :             matchesNeeded--;
    1282           0 :             if (matchesNeeded <= 0) { return true; }
    1283             :         }
    1284             :         // No? well, keep trying...
    1285           0 :         goto Next_Move;
    1286             : 
    1287           0 :       Check_Promotions:
    1288             :         // We only continue if this game has promotion moves:
    1289           0 :         if (! PromotionsFlag) { return false; }
    1290             : 
    1291           0 :       Next_Move:
    1292           0 :         if (buf == NULL) {
    1293           0 :             MoveForward();
    1294           0 :             if (CurrentMove->marker == END_MARKER) {
    1295           0 :                 err = ERROR_EndOfMoveList;
    1296             :             }
    1297             :         } else {
    1298           0 :             err = DecodeNextMove (buf, NULL);
    1299             :         }
    1300           0 :         plyCount++;
    1301           0 :         if (! foundMatch) { matchesNeeded = matchLength; }
    1302             :     }
    1303             : 
    1304             :     // End of game reached, and no match:
    1305           0 :     return false;
    1306             : }
    1307             : 
    1308             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1309             : // Game::ExactMatch():
    1310             : //      Exact position search test.
    1311             : //      If sm is not NULL, its from, to, promote etc will be filled with
    1312             : //      the next move at the matching position, if there is one.
    1313             : //      If neverMatch is non-NULL, the boolean it points to is set to
    1314             : //      true if the game could never match even with extra moves.
    1315             : //
    1316             : bool
    1317           0 : Game::ExactMatch (Position * searchPos, ByteBuffer * buf, simpleMoveT * sm,
    1318             :                   gameExactMatchT searchType, bool * neverMatch)
    1319             : {
    1320             :     // If buf is NULL, the game is in memory. Otherwise, Decode only
    1321             :     // the necessary moves:
    1322           0 :     errorT err = OK;
    1323             : 
    1324           0 :     if (buf == NULL) {
    1325           0 :         MoveToPly(0);
    1326             :     } else {
    1327           0 :         Clear ();
    1328           0 :         err = DecodeStart (buf);
    1329           0 :         KeepDecodedMoves = false;
    1330             :     }
    1331             : 
    1332           0 :     uint plyCount = 0;
    1333             :     //uint skip = 0;    // Just for statistics on number of moves skipped.
    1334           0 :     uint search_whiteHPawns = 0;
    1335           0 :     uint search_blackHPawns = 0;
    1336             :     uint current_whiteHPawns, current_blackHPawns;
    1337             :     bool check_pawnMaskWhite, check_pawnMaskBlack;
    1338           0 :     bool doHomePawnChecks = false;
    1339             : 
    1340           0 :     uint wpawnFyle [8] = {0, 0, 0, 0, 0, 0, 0, 0};
    1341           0 :     uint bpawnFyle [8] = {0, 0, 0, 0, 0, 0, 0, 0};;
    1342             : 
    1343           0 :     if (searchType == GAME_EXACT_MATCH_Fyles) {
    1344           0 :         const pieceT* board = searchPos->GetBoard();
    1345           0 :         uint fyle = 0;
    1346           0 :         for (squareT sq = A1; sq <= H8; sq++, board++) {
    1347           0 :             if (*board == WP) {
    1348           0 :                 wpawnFyle[fyle]++;
    1349           0 :             } else if (*board == BP) {
    1350           0 :                 bpawnFyle[fyle]++;
    1351             :             }
    1352           0 :             fyle = (fyle + 1) & 7;
    1353             :         }
    1354             :     }
    1355             : 
    1356             :     // If neverMatch is null, point it at a dummy value
    1357             :     bool dummy;
    1358           0 :     if (neverMatch == NULL) { neverMatch = &dummy; }
    1359           0 :     *neverMatch = false;
    1360             : 
    1361           0 :     if (searchType == GAME_EXACT_MATCH_Exact  ||
    1362             :         searchType == GAME_EXACT_MATCH_Pawns) {
    1363           0 :         doHomePawnChecks = true;
    1364           0 :         search_whiteHPawns = calcHomePawnMask (WP, searchPos->GetBoard());
    1365           0 :         search_blackHPawns = calcHomePawnMask (BP, searchPos->GetBoard());
    1366             :     }
    1367           0 :     check_pawnMaskWhite = check_pawnMaskBlack = false;
    1368             : 
    1369           0 :     while (err == OK) {
    1370           0 :         const pieceT* currentBoard = CurrentPos->GetBoard();
    1371           0 :         const pieceT* board = searchPos->GetBoard();
    1372           0 :         const pieceT* b1 = currentBoard;
    1373           0 :         const pieceT* b2 = board;
    1374           0 :         bool found = true;
    1375             : 
    1376             :         // If NO_SPEEDUPS is defined, a slower search is done without
    1377             :         // optimisations that detect insufficient material.
    1378             : #ifndef NO_SPEEDUPS
    1379             :         // Insufficient material optimisation:
    1380           0 :         if (searchPos->GetCount(WHITE) > CurrentPos->GetCount(WHITE)  ||
    1381           0 :             searchPos->GetCount(BLACK) > CurrentPos->GetCount(BLACK)) {
    1382           0 :             *neverMatch = true;
    1383           0 :             return false;
    1384             :         }
    1385             :         // Insufficient pawns optimisation:
    1386           0 :         if (searchPos->PieceCount(WP) > CurrentPos->PieceCount(WP)  ||
    1387           0 :             searchPos->PieceCount(BP) > CurrentPos->PieceCount(BP)) {
    1388           0 :             *neverMatch = true;
    1389           0 :             return false;
    1390             :         }
    1391             : 
    1392             :         // HomePawn mask optimisation:
    1393             :         // If current pos doesn't have a pawn on home rank where
    1394             :         // the search pos has one, it can never match.
    1395             :         // This happens when (current_xxHPawns & search_xxHPawns) is
    1396             :         // not equal to search_xxHPawns.
    1397             :         // We do not do this optimisation for a pawn files search,
    1398             :         // because the exact pawn squares are not important there.
    1399             : 
    1400           0 :         if (searchType != GAME_EXACT_MATCH_Fyles) {
    1401           0 :             if (check_pawnMaskWhite) {
    1402           0 :                 current_whiteHPawns = calcHomePawnMask (WP, currentBoard);
    1403           0 :                 if ((current_whiteHPawns & search_whiteHPawns)
    1404             :                         != search_whiteHPawns) {
    1405           0 :                     *neverMatch = true;
    1406           0 :                     return false;
    1407             :                 }
    1408             :             }
    1409           0 :             if (check_pawnMaskBlack) {
    1410           0 :                 current_blackHPawns = calcHomePawnMask (BP, currentBoard);
    1411           0 :                 if ((current_blackHPawns & search_blackHPawns)
    1412             :                         != search_blackHPawns) {
    1413           0 :                     *neverMatch = true;
    1414           0 :                     return false;
    1415             :                 }
    1416             :             }
    1417             :         }
    1418             : #endif  // #ifndef NO_SPEEDUPS
    1419             : 
    1420             :         // Not correct color: skip to next move
    1421           0 :         if (searchPos->GetToMove() != CurrentPos->GetToMove()) {
    1422             :             //skip++;
    1423           0 :             goto Move_Forward;
    1424             :         }
    1425             : 
    1426             :         // Extra material: skip to next move
    1427           0 :         if (searchPos->GetCount(WHITE) < CurrentPos->GetCount(WHITE)  ||
    1428           0 :             searchPos->GetCount(BLACK) < CurrentPos->GetCount(BLACK)) {
    1429             :             //skip++;
    1430           0 :             goto Move_Forward;
    1431             :         }
    1432             :         // Extra pawns/pieces: skip to next move
    1433           0 :         if (searchPos->PieceCount(WP) != CurrentPos->PieceCount(WP)  ||
    1434           0 :             searchPos->PieceCount(BP) != CurrentPos->PieceCount(BP)  ||
    1435           0 :             searchPos->PieceCount(WN) != CurrentPos->PieceCount(WN)  ||
    1436           0 :             searchPos->PieceCount(BN) != CurrentPos->PieceCount(BN)  ||
    1437           0 :             searchPos->PieceCount(WB) != CurrentPos->PieceCount(WB)  ||
    1438           0 :             searchPos->PieceCount(BB) != CurrentPos->PieceCount(BB)  ||
    1439           0 :             searchPos->PieceCount(WR) != CurrentPos->PieceCount(WR)  ||
    1440           0 :             searchPos->PieceCount(BR) != CurrentPos->PieceCount(BR)  ||
    1441           0 :             searchPos->PieceCount(WQ) != CurrentPos->PieceCount(WQ)  ||
    1442           0 :             searchPos->PieceCount(BQ) != CurrentPos->PieceCount(BQ)) {
    1443             :             //skip++;
    1444           0 :             goto Move_Forward;
    1445             :         }
    1446             : 
    1447             :         // NOW, compare the actual boards piece-by-piece.
    1448           0 :         if (searchType == GAME_EXACT_MATCH_Exact) {
    1449           0 :             if (searchPos->HashValue() == CurrentPos->HashValue()) {
    1450           0 :                 for (squareT sq = A1;  sq <= H8;  sq++, b1++, b2++) {
    1451           0 :                     if (*b1 != *b2) { found = false; break; }
    1452             :                 }
    1453             :             } else {
    1454           0 :                 found = false;
    1455             :             }
    1456           0 :         } else if (searchType == GAME_EXACT_MATCH_Pawns) {
    1457           0 :             if (searchPos->PawnHashValue() == CurrentPos->PawnHashValue()) {
    1458           0 :                 for (squareT sq = A1;  sq <= H8;  sq++, b1++, b2++) {
    1459           0 :                     if (*b1 != *b2  &&  (*b1 == WP  ||  *b1 == BP)) {
    1460           0 :                         found = false;
    1461           0 :                         break;
    1462             :                     }
    1463             :                 }
    1464             :             } else {
    1465           0 :                 found = false;
    1466             :             }
    1467           0 :         } else if (searchType == GAME_EXACT_MATCH_Fyles) {
    1468           0 :             for (fyleT f = A_FYLE; f <= H_FYLE; f++) {
    1469           0 :                 if (searchPos->FyleCount(WP,f) != CurrentPos->FyleCount(WP,f)
    1470           0 :                       || searchPos->FyleCount(BP,f) != CurrentPos->FyleCount(BP,f)) {
    1471           0 :                     found = false;
    1472           0 :                     break;
    1473             :                 }
    1474             :             }
    1475             :         } else {
    1476             :             // searchType == GAME_EXACT_Match_Material, so do nothing.
    1477             :         }
    1478             : 
    1479           0 :         if (found) {
    1480             :             // Found a match! Set the returned next-move:
    1481           0 :             if (sm) {  // We need to decode the next move.
    1482           0 :                 if (buf == NULL) {
    1483           0 :                     MoveForward();
    1484           0 :                     if (CurrentMove->marker == END_MARKER) {
    1485             :                         // Position matched at last move in the game.
    1486           0 :                         sm->from = sm->to = NULL_SQUARE;
    1487           0 :                         sm->promote = EMPTY;
    1488             :                     } else {
    1489           0 :                         *sm = CurrentMove->prev->moveData;
    1490           0 :                         MoveBackup();
    1491             :                     }
    1492             :                 } else {
    1493           0 :                     err = DecodeNextMove (buf, sm);
    1494           0 :                     if (err != OK) {
    1495             :                         // Position matched at last move in the game.
    1496           0 :                         sm->from = sm->to = NULL_SQUARE;
    1497           0 :                         sm->promote = EMPTY;
    1498             :                     } else {
    1499             :                         // Backup to the matching position:
    1500           0 :                         CurrentPos->UndoSimpleMove (sm);
    1501             :                     }
    1502             :                 }
    1503             :             }
    1504           0 :             return true;
    1505             :         }
    1506             : 
    1507           0 :     Move_Forward:
    1508             : #ifndef NO_SPEEDUPS
    1509           0 :         if (doHomePawnChecks) {
    1510           0 :             check_pawnMaskWhite = false;
    1511           0 :             check_pawnMaskBlack = false;
    1512           0 :             rankT rTo = square_Rank (CurrentMove->moveData.to);
    1513           0 :             rankT rFrom = square_Rank (CurrentMove->moveData.from);
    1514             :             // We only re-check the home pawn masks when something moves
    1515             :             // to or from the 2nd/7th rank:
    1516           0 :             if (rTo == RANK_2  ||  rFrom == RANK_2) {
    1517           0 :                 check_pawnMaskWhite = true;
    1518             :             }
    1519           0 :             if (rTo == RANK_7  ||  rFrom == RANK_7) {
    1520           0 :                 check_pawnMaskBlack = true;
    1521             :             }
    1522             :         }
    1523             : #endif
    1524           0 :         if (buf == NULL) {
    1525           0 :             MoveForward ();
    1526           0 :             if (CurrentMove->marker == END_MARKER) {
    1527           0 :                 err = ERROR_EndOfMoveList;
    1528             :             }
    1529             :         } else {
    1530           0 :             err = DecodeNextMove (buf, NULL);
    1531           0 :             if (err != OK  &&  err != ERROR_EndOfMoveList) {
    1532           0 :                 return false;
    1533             :             }
    1534             :         }
    1535           0 :         plyCount++;
    1536             :     }
    1537           0 :     return false;
    1538             : }
    1539             : 
    1540             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1541             : // Game::VarExactMatch():
    1542             : //    Like ExactMatch(), but also searches in variations.
    1543             : //    This is much slower than ExactMatch(), since it will
    1544             : //    search every position until a match is found.
    1545             : bool
    1546           0 : Game::VarExactMatch (Position * searchPos, gameExactMatchT searchType)
    1547             : {
    1548           0 :     uint wpawnFyle [8] = {0, 0, 0, 0, 0, 0, 0, 0};
    1549           0 :     uint bpawnFyle [8] = {0, 0, 0, 0, 0, 0, 0, 0};;
    1550             : 
    1551           0 :     if (searchType == GAME_EXACT_MATCH_Fyles) {
    1552           0 :         const pieceT* board = searchPos->GetBoard();
    1553           0 :         uint fyle = 0;
    1554           0 :         for (squareT sq = A1; sq <= H8; sq++, board++) {
    1555           0 :             if (*board == WP) {
    1556           0 :                 wpawnFyle[fyle]++;
    1557           0 :             } else if (*board == BP) {
    1558           0 :                 bpawnFyle[fyle]++;
    1559             :             }
    1560           0 :             fyle = (fyle + 1) & 7;
    1561             :         }
    1562             :     }
    1563             : 
    1564           0 :     errorT err = OK;
    1565           0 :     while (err == OK) {
    1566             :         // Check if this position matches:
    1567           0 :         bool match = false;
    1568           0 :         if (searchPos->GetToMove() == CurrentPos->GetToMove()
    1569           0 :             &&  searchPos->GetCount(WHITE) == CurrentPos->GetCount(WHITE)
    1570           0 :             &&  searchPos->GetCount(BLACK) == CurrentPos->GetCount(BLACK)
    1571           0 :             &&  searchPos->PieceCount(WP) == CurrentPos->PieceCount(WP)
    1572           0 :             &&  searchPos->PieceCount(BP) == CurrentPos->PieceCount(BP)
    1573           0 :             &&  searchPos->PieceCount(WN) == CurrentPos->PieceCount(WN)
    1574           0 :             &&  searchPos->PieceCount(BN) == CurrentPos->PieceCount(BN)
    1575           0 :             &&  searchPos->PieceCount(WB) == CurrentPos->PieceCount(WB)
    1576           0 :             &&  searchPos->PieceCount(BB) == CurrentPos->PieceCount(BB)
    1577           0 :             &&  searchPos->PieceCount(WR) == CurrentPos->PieceCount(WR)
    1578           0 :             &&  searchPos->PieceCount(BR) == CurrentPos->PieceCount(BR)
    1579           0 :             &&  searchPos->PieceCount(WQ) == CurrentPos->PieceCount(WQ)
    1580           0 :             &&  searchPos->PieceCount(BQ) == CurrentPos->PieceCount(BQ)) {
    1581           0 :             match = true;
    1582           0 :             const pieceT* b1 = CurrentPos->GetBoard();
    1583           0 :             const pieceT* b2 = searchPos->GetBoard();
    1584           0 :             if (searchType == GAME_EXACT_MATCH_Pawns) {
    1585           0 :                 for (squareT sq = A1;  sq <= H8;  sq++, b1++, b2++) {
    1586           0 :                     if (*b1 != *b2  &&  (*b1 == WP  ||  *b1 == BP)) {
    1587           0 :                         match = false; break;
    1588             :                     }
    1589             :                 }
    1590           0 :             } else if (searchType == GAME_EXACT_MATCH_Fyles) {
    1591           0 :                 uint wpf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
    1592           0 :                 uint bpf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
    1593           0 :                 uint fyle = 0;
    1594           0 :                 for (squareT sq = A1;  sq <= H8;  sq++, b1++) {
    1595           0 :                     if (*b1 == WP) {
    1596           0 :                         wpf[fyle]++;
    1597           0 :                         if (wpf[fyle] > wpawnFyle[fyle]) { match = false; break; }
    1598           0 :                     } else if (*b1 == BP) {
    1599           0 :                         bpf[fyle]++;
    1600           0 :                         if (bpf[fyle] > bpawnFyle[fyle]) { match = false; break; }
    1601             :                     }
    1602           0 :                     fyle = (fyle + 1) & 7;
    1603             :                 }
    1604           0 :             } else if (searchType == GAME_EXACT_MATCH_Exact) {
    1605           0 :                 if (searchPos->HashValue() == CurrentPos->HashValue()) {
    1606           0 :                     for (squareT sq = A1;  sq <= H8;  sq++, b1++, b2++) {
    1607           0 :                         if (*b1 != *b2) { match = false; break; }
    1608             :                     }
    1609             :                 } else {
    1610           0 :                     match = false;
    1611             :                 }
    1612             :             } else {
    1613             :                 // searchType == GAME_EXACT_MATCH_Material, so do nothing.
    1614             :             }
    1615             :         }
    1616           0 :         if (match) { return true; }
    1617             : 
    1618             :         // Now try searching each variation in turn:
    1619           0 :         for (uint i=0; i < CurrentMove->numVariations; i++) {
    1620           0 :             MoveIntoVariation (i);
    1621           0 :             match = VarExactMatch (searchPos, searchType);
    1622           0 :             MoveExitVariation();
    1623           0 :             if (match) { return true; }
    1624             :         }
    1625             :         // Continue down this variation:
    1626           0 :         MoveForward();
    1627           0 :         if (CurrentMove->marker == END_MARKER) {
    1628           0 :             err = ERROR_EndOfMoveList;
    1629             :         }
    1630             :     }
    1631           0 :     return false;
    1632             : }
    1633             : 
    1634             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1635             : // Game::GetPartialMoveList():
    1636             : //      Write the first few moves of a game.
    1637             : //
    1638             : errorT
    1639           0 : Game::GetPartialMoveList (DString * outStr, uint plyCount)
    1640             : {
    1641             :     // First, copy the relevant data so we can leave the game state
    1642             :     // unaltered:
    1643           0 :     auto location = currentLocation();
    1644             : 
    1645           0 :     MoveToPly(0);
    1646             :     char temp [80];
    1647           0 :     for (uint i=0; i < plyCount; i++) {
    1648           0 :         if (CurrentMove->marker == END_MARKER) {
    1649           0 :             break;
    1650             :         }
    1651           0 :         if (i != 0) { outStr->Append (" "); }
    1652           0 :         if (i == 0  ||  CurrentPos->GetToMove() == WHITE) {
    1653           0 :             sprintf (temp, "%d%s", CurrentPos->GetFullMoveCount(),
    1654           0 :                      (CurrentPos->GetToMove() == WHITE ? "." : "..."));
    1655           0 :             outStr->Append (temp);
    1656             :         }
    1657           0 :         moveT * m = CurrentMove;
    1658           0 :         if (m->san[0] == 0) {
    1659           0 :             CurrentPos->MakeSANString(&(m->moveData),
    1660             :                                       m->san, SAN_CHECKTEST);
    1661             :         }
    1662             :         // add one space for indenting to work out right
    1663           0 :         outStr->Append (" ");
    1664           0 :         outStr->Append (m->san);
    1665           0 :         MoveForward();
    1666             :     }
    1667             : 
    1668             :     // Now reconstruct the original game state:
    1669           0 :     restoreLocation(location);
    1670           0 :     return OK;
    1671             : }
    1672             : 
    1673             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1674             : // Returns the SAN representation of the next move or an empty string ("") if
    1675             : // not at a move.
    1676        3168 : const char* Game::GetNextSAN() {
    1677        3168 :         ASSERT(!CurrentMove->endMarker() || *CurrentMove->san == '\0');
    1678             : 
    1679        3168 :         if (!CurrentMove->endMarker() && *CurrentMove->san == '\0') {
    1680        5752 :                 CurrentPos->MakeSANString(
    1681        2876 :                     &CurrentMove->moveData, CurrentMove->san,
    1682        1438 :                     CurrentMove->next->endMarker() ? SAN_MATETEST : SAN_CHECKTEST);
    1683             :         }
    1684        3168 :         return CurrentMove->san;
    1685             : }
    1686             : 
    1687             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1688             : // Game::GetSAN():
    1689             : //      Print the SAN representation of the current move to a string.
    1690             : //      Prints an empty string ("") if not at a move.
    1691           6 : void Game::GetSAN(char* str) {
    1692           6 :         ASSERT(str != NULL);
    1693           6 :         strcpy(str, GetNextSAN());
    1694           6 : }
    1695             : 
    1696             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1697             : // Game::GetPrevSAN():
    1698             : //      Print the SAN representation of the current move to a string.
    1699             : //      Prints an empty string ("") if not at a move.
    1700             : void
    1701        1124 : Game::GetPrevSAN (char * str)
    1702             : {
    1703        1124 :     ASSERT (str != NULL);
    1704        1124 :     moveT * m = CurrentMove->prev;
    1705        1124 :     if (m->marker == START_MARKER  ||  m->marker == END_MARKER) {
    1706          12 :         str[0] = 0;
    1707          12 :         return;
    1708             :     }
    1709        1112 :     if (m->san[0] == 0) {
    1710        1112 :         MoveBackup();
    1711        1112 :         CurrentPos->MakeSANString (&(m->moveData), m->san, SAN_MATETEST);
    1712        1112 :         MoveForward();
    1713             :     }
    1714        1112 :     strcpy (str, m->san);
    1715             : }
    1716             : 
    1717             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1718             : // Game::GetPrevMoveUCI():
    1719             : //      Print the UCI representation of the current move to a string.
    1720             : //      Prints an empty string ("") if not at a move.
    1721           0 : void Game::GetPrevMoveUCI(char* str) const {
    1722           0 :     ASSERT(str != NULL);
    1723           0 :     moveT* m = CurrentMove->prev;
    1724           0 :     if (m->marker == START_MARKER) {
    1725           0 :         str[0] = 0;
    1726             :     } else {
    1727           0 :         Position pos(*CurrentPos);
    1728           0 :         pos.UndoSimpleMove(&m->moveData);
    1729           0 :         pos.MakeUCIString(&m->moveData, str);
    1730             :     }
    1731           0 : }
    1732             : 
    1733             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1734             : // Game::GetNextMoveUCI():
    1735             : //      Print the UCI representation of the next move to a string.
    1736             : //      Prints an empty string ("") if not at a move.
    1737             : void
    1738           0 : Game::GetNextMoveUCI (char * str)
    1739             : {
    1740           0 :     ASSERT (str != NULL);
    1741           0 :     moveT * m = CurrentMove;
    1742           0 :     if (m->marker == START_MARKER  ||  m->marker == END_MARKER) {
    1743           0 :       str[0] = 0;
    1744           0 :       return;
    1745             :     }
    1746           0 :     CurrentPos->MakeUCIString (&(m->moveData), str);
    1747             : }
    1748             : 
    1749             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1750             : // commentEmpty:
    1751             : //    Called by WriteMoveList to check there is really
    1752             : //    something to print given display options.
    1753             : //    comment is supposed to be non null
    1754             : bool
    1755      223469 : Game::CommentEmpty ( const char * comment)
    1756             : {
    1757      223469 :     char * s = NULL;
    1758      223469 :     bool ret = false;
    1759             : 
    1760      223469 :     if (comment == NULL)
    1761           0 :       return true;
    1762             : 
    1763      223469 :     if (comment[0] == '\0')
    1764           0 :       return true;
    1765             : 
    1766      223469 :     if (PgnStyle & PGN_STYLE_STRIP_MARKS) {
    1767           0 :       s = strDuplicate (comment);
    1768           0 :       strTrimMarkCodes (s);
    1769           0 :       char * tmp = s;
    1770           0 :       bool empty = true;
    1771           0 :       while (tmp[0] != 0) {
    1772           0 :         if (tmp[0] != ' ') {
    1773           0 :           empty = false;
    1774           0 :           break;
    1775             :         }
    1776           0 :         tmp++;
    1777             :       }
    1778           0 :       ret = empty;
    1779             : 
    1780           0 :       delete[] s;
    1781             :     }
    1782             : 
    1783      223469 :     return ret;
    1784             : }
    1785             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1786             : // writeComment:
    1787             : //    Called by WriteMoveList to write a single comment.
    1788             : void
    1789      223518 : Game::WriteComment (TextBuffer * tb, const char * preStr,
    1790             :               const char * comment, const char * postStr)
    1791             : {
    1792             : 
    1793      223518 :     char * s = NULL;
    1794             : 
    1795      223518 :     if (PgnStyle & PGN_STYLE_STRIP_MARKS) {
    1796           0 :         s = strDuplicate (comment);
    1797           0 :         strTrimMarkCodes (s);
    1798             :     } else {
    1799      223518 :       s = (char *) comment;
    1800             :     }
    1801             : 
    1802      223518 :     if (s[0] != '\0') {
    1803             : 
    1804      223518 :         if (IsColorFormat()) {
    1805           0 :             tb->PrintString ("<c_");
    1806           0 :             tb->PrintInt (NumMovesPrinted);
    1807           0 :             tb->PrintChar ('>');
    1808             :         }
    1809             : 
    1810      223518 :         if (IsColorFormat()) {
    1811             :             // Translate "<", ">" in comments:
    1812           0 :             tb->AddTranslation ('<', "<lt>");
    1813           0 :             tb->AddTranslation ('>', "<gt>");
    1814             :             // S.A any issues ?
    1815           0 :             tb->NewlinesToSpaces (0);
    1816           0 :             tb->PrintString (s);
    1817           0 :             tb->ClearTranslation ('<');
    1818           0 :             tb->ClearTranslation ('>');
    1819             :         } else {
    1820      223518 :             tb->PrintString (preStr);
    1821      223518 :             tb->PrintString (s);
    1822      223518 :             tb->PrintString (postStr);
    1823             :         }
    1824             : 
    1825      223518 :         if (IsColorFormat()) { tb->PrintString ("</c>"); }
    1826             :     }
    1827             : 
    1828      223518 :     if (PgnStyle & PGN_STYLE_STRIP_MARKS) {
    1829           0 :         delete[] s;
    1830             :     }
    1831      223518 : }
    1832             : 
    1833             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1834             : // Game::WriteMoveList():
    1835             : //      Write the moves, variations and comments in PGN notation.
    1836             : //      Recursive; calls itself to write variations.
    1837             : //
    1838      310515 : errorT Game::WriteMoveList(TextBuffer* tb, moveT* oldCurrentMove,
    1839             :                            bool printMoveNum, bool inComment) {
    1840             :     char tempTrans[10];
    1841      310515 :     const char * preCommentStr = "{";
    1842      310515 :     const char * postCommentStr = "}";
    1843      310515 :     const char * startTable = "\n";
    1844      310515 :     const char * startColumn = "\t";
    1845      310515 :     const char * nextColumn = "\t";
    1846      310515 :     const char * endColumn = "\n";
    1847      310515 :     const char * endTable = "\n";
    1848      310515 :     bool printDiagrams = false;
    1849             : 
    1850      310515 :     if (IsHtmlFormat()) {
    1851           0 :         preCommentStr = "";
    1852           0 :         postCommentStr = "";
    1853           0 :         startTable = "<table width=\"50%\">\n";
    1854           0 :         startColumn = "<tr align=left>\n  <td width=\"15%\"><b>";
    1855           0 :         nextColumn = "</b></td>\n  <td width=\"45%\" align=left><b>";
    1856           0 :         endColumn = "</b></td>\n</tr>\n";
    1857           0 :         endTable = "</table>\n";
    1858           0 :         printDiagrams = true;
    1859             :     }
    1860      310515 :     if (IsLatexFormat()) {
    1861           0 :         preCommentStr = "\\begin{nochess}{\\rm ";
    1862           0 :         postCommentStr = "}\\end{nochess}";
    1863           0 :         startTable = "\n\\begin{tabular}{p{1cm}p{2cm}p{2cm}}\n";
    1864           0 :         startColumn = "";
    1865           0 :         nextColumn = "&";
    1866           0 :         endColumn = "\\\\\n";
    1867           0 :         endTable = "\\end{tabular}\n\n";
    1868           0 :         printDiagrams = true;
    1869             :     }
    1870      310515 :     if (IsColorFormat()) {
    1871           0 :         startTable = "<br>";
    1872           0 :         endColumn = "<br>";
    1873             :     }
    1874             : 
    1875      310515 :     if (IsHtmlFormat()  &&  VarDepth == 0) { tb->PrintString ("<b>"); }
    1876      310515 :     if ((PgnStyle & PGN_STYLE_COLUMN)  &&  VarDepth == 0) {
    1877           0 :         tb->PrintString (startTable);
    1878             :     }
    1879             : 
    1880      310515 :     if (IsPlainFormat()  &&  inComment) {
    1881           0 :         preCommentStr = "";
    1882           0 :         postCommentStr = "";
    1883             :     }
    1884      310515 :     moveT * m = CurrentMove;
    1885             : 
    1886             :     // Print null moves:
    1887      621030 :     if ((PgnStyle & PGN_STYLE_NO_NULL_MOVES) && !inComment &&
    1888      310515 :             IsPlainFormat() && m->isNull()) {
    1889           0 :         inComment = true;
    1890           0 :         tb->PrintString(preCommentStr);
    1891           0 :         preCommentStr = "";
    1892           0 :         postCommentStr = "";
    1893             :     }
    1894             : 
    1895             :     // If this is a variation and it starts with a comment, print it:
    1896      621030 :     if ((VarDepth > 0 || CurrentMove->prev == FirstMove) && 
    1897      310515 :             ! CurrentMove->prev->comment.empty()) {
    1898          49 :         if (PgnStyle & PGN_STYLE_COMMENTS) {
    1899          49 :             WriteComment (tb, preCommentStr, CurrentMove->prev->comment.c_str(),
    1900             :                           postCommentStr);
    1901          49 :             tb->PrintSpace();
    1902          49 :             if (!VarDepth) {
    1903           1 :                 tb->ClearTranslation ('\n');
    1904           1 :                 tb->NewLine();
    1905           1 :                 if (IsColorFormat() || IsLatexFormat()) {
    1906           0 :                     tb->NewLine();
    1907             :                 }
    1908             :             }
    1909             :         }
    1910             :     }
    1911             : 
    1912     3434459 :     while (CurrentMove->marker != END_MARKER) {
    1913     1561972 :         moveT *m = CurrentMove;
    1914     1561972 :         bool commentLine = false;
    1915             : 
    1916     1561972 :         if (m->san[0] == 0) {
    1917             :             // If there is a next move we can skip the SAN_MATETEST
    1918     3123936 :             CurrentPos->MakeSANString(
    1919             :                 &(m->moveData), m->san,
    1920     1561968 :                 (m->next->marker != END_MARKER) ? SAN_CHECKTEST : SAN_MATETEST);
    1921             :         }
    1922             : 
    1923     1561972 :         bool printThisMove = true;
    1924     1561972 :         if (m->isNull()) {
    1925             :             // Null moves are not printed in LaTeX or HTML:
    1926           0 :             if (IsLatexFormat()  ||  IsHtmlFormat()) {
    1927           0 :                 printThisMove = false;
    1928           0 :                 printMoveNum = true;
    1929             :             }
    1930             :             // If Plain PGN format, check whether to convert the
    1931             :             // null move and remainder of the line to a comment:
    1932           0 :             if ((PgnStyle & PGN_STYLE_NO_NULL_MOVES)  &&  IsPlainFormat()) {
    1933           0 :                 if (!inComment) {
    1934             :                     // Enter inComment mode to convert rest of line
    1935             :                     // to a comment:
    1936           0 :                     inComment = true;
    1937           0 :                     tb->PrintString(preCommentStr);
    1938           0 :                     preCommentStr = "";
    1939           0 :                     postCommentStr = "";
    1940             :                 }
    1941           0 :                 printThisMove = false;
    1942           0 :                 printMoveNum = true;
    1943             :             }
    1944             :         }
    1945     1561972 :         int colWidth = 6;
    1946     1561972 :         NumMovesPrinted++;
    1947             : 
    1948     1561972 :         if (printThisMove) {
    1949             :         // Print the move number and following dots if necessary:
    1950     1561972 :         if (IsColorFormat()) {
    1951           0 :             tb->PrintString ("<m_");
    1952           0 :             tb->PrintInt (NumMovesPrinted);
    1953           0 :             tb->PrintChar ('>');
    1954             :         }
    1955     1561972 :         if (printMoveNum  ||  (CurrentPos->GetToMove() == WHITE)) {
    1956     1155998 :             if ((PgnStyle & PGN_STYLE_COLUMN)  &&  VarDepth == 0) {
    1957           0 :                 tb->PrintString (startColumn);
    1958             :                 char temp [10];
    1959           0 :                 sprintf (temp, "%4u.", CurrentPos->GetFullMoveCount());
    1960           0 :                 tb->PrintString (temp);
    1961           0 :                 if (CurrentPos->GetToMove() == BLACK) {
    1962           0 :                     tb->PauseTranslations();
    1963           0 :                     tb->PrintString (nextColumn);
    1964           0 :                     tb->PrintString ("...");
    1965           0 :                     if (IsPlainFormat()  ||  IsColorFormat()) {
    1966           0 :                         tb->PrintString ("        ");
    1967             :                     }
    1968           0 :                     tb->ResumeTranslations();
    1969           0 :                 }
    1970             :             } else {
    1971     1155998 :             if (PgnStyle & PGN_STYLE_MOVENUM_SPACE) {
    1972           0 :                 tb->PrintInt(CurrentPos->GetFullMoveCount(), (CurrentPos->GetToMove() == WHITE ? "." : ". ..."));
    1973             :                 } else {
    1974     1155998 :                     tb->PrintInt(CurrentPos->GetFullMoveCount(), (CurrentPos->GetToMove() == WHITE ? "." : "..."));
    1975             :                 }
    1976     1155998 :                 if (PgnStyle & PGN_STYLE_MOVENUM_SPACE) {
    1977           0 :                     if (IsLatexFormat()) {
    1978           0 :                         tb->PrintChar ('~');
    1979             :                     } else {
    1980           0 :                         tb->PrintChar (' ');
    1981             :                     }
    1982             :                 }
    1983             :             }
    1984     1155998 :             printMoveNum = false;
    1985             :         }
    1986             : 
    1987             :         // Now print the move: only regenerate the SAN string if necessary.
    1988             : 
    1989     1561972 :         if ((PgnStyle & PGN_STYLE_COLUMN)  &&  VarDepth == 0) {
    1990           0 :             tb->PauseTranslations();
    1991           0 :             tb->PrintString (nextColumn);
    1992           0 :             tb->ResumeTranslations();
    1993             :         }
    1994     1561972 :         if (IsColorFormat() && (PgnStyle & PGN_STYLE_UNICODE)) {
    1995             :             char buf[100];
    1996           0 :             char* q = buf;
    1997             : 
    1998           0 :             for (char const* p = m->san; *p; ++p) {
    1999           0 :                 ASSERT(q - buf < static_cast<std::ptrdiff_t>(sizeof(buf) - 4));
    2000             : 
    2001           0 :                 switch (*p) {
    2002           0 :                     case 'K':    q = std::copy_n("\xe2\x99\x94", 3, q); break;
    2003           0 :                     case 'Q':    q = std::copy_n("\xe2\x99\x95", 3, q); break;
    2004           0 :                     case 'R':    q = std::copy_n("\xe2\x99\x96", 3, q); break;
    2005           0 :                     case 'B':    q = std::copy_n("\xe2\x99\x97", 3, q); break;
    2006           0 :                     case 'N':    q = std::copy_n("\xe2\x99\x98", 3, q); break;
    2007           0 :                     case 'P':    q = std::copy_n("\xe2\x99\x99", 3, q); break;
    2008           0 :                     default:    *q++ = *p; break;
    2009             :                 }
    2010             : 
    2011             :             }
    2012           0 :             *q = '\0';
    2013           0 :             tb->PrintWord (buf);
    2014             :         } else {
    2015             :             // translate pieces
    2016     1561972 :             strcpy(tempTrans, m->san);
    2017     1561972 :             transPieces(tempTrans);
    2018             :             //tb->PrintWord (m->san);
    2019     1561972 :             tb->PrintWord (tempTrans);
    2020             :         }
    2021     1561972 :         colWidth -= (int) std::strlen(m->san);
    2022     1561972 :         if (IsColorFormat()) {
    2023           0 :             tb->PrintString ("</m>");
    2024             :         }
    2025             :         }
    2026             : 
    2027     1561972 :         bool endedColumn = false;
    2028             : 
    2029             :         // Print NAGs and comments if the style indicates:
    2030             : 
    2031     1561972 :         if (PgnStyle & PGN_STYLE_COMMENTS) {
    2032     1561972 :             bool printDiagramHere = false;
    2033     1561972 :             if (IsColorFormat()  &&  m->nagCount > 0) {
    2034           0 :                 tb->PrintString ("<nag>");
    2035             :             }
    2036     1562260 :             for (uint i = 0; i < (uint) m->nagCount; i++) {
    2037             :                 char temp[20];
    2038         288 :                 game_printNag (m->nags[i], temp, PgnStyle & PGN_STYLE_SYMBOLS,
    2039             :                                PgnFormat);
    2040             : 
    2041             :                 // Do not print a space before the Nag if it is the
    2042             :                 // first nag and starts with "!" or "?" -- those symbols
    2043             :                 // look better printed next to the move:
    2044             : 
    2045         288 :                 if (i > 0  ||  (temp[0] != '!'  &&  temp[0] != '?')) {
    2046         288 :                     tb->PrintSpace();
    2047         288 :                     colWidth--;
    2048             :                 }
    2049         288 :                 if (printDiagrams  &&  m->nags[i] == NAG_Diagram) {
    2050           0 :                     printDiagramHere = true;
    2051             :                 }
    2052         288 :                 tb->PrintWord (temp);
    2053         288 :                 colWidth -= (int) std::strlen(temp);
    2054             : 
    2055             :             }
    2056     1561972 :             if (IsColorFormat()  &&  m->nagCount > 0) {
    2057           0 :                 tb->PrintString ("</nag>");
    2058             :             }
    2059     1561972 :             tb->PrintSpace();
    2060     1561972 :             colWidth--;
    2061     1561972 :             if ((PgnStyle & PGN_STYLE_COLUMN)  &&  VarDepth == 0) {
    2062           0 :                 if (IsPlainFormat()  ||  IsColorFormat()) {
    2063           0 :                     while (colWidth-- > 0) { tb->PrintSpace(); }
    2064             :                 }
    2065             :             }
    2066             : 
    2067     1561972 :             if (printDiagramHere) {
    2068           0 :                 if ((PgnStyle & PGN_STYLE_COLUMN)  &&  VarDepth == 0) {
    2069           0 :                     if (! endedColumn) {
    2070           0 :                         if (CurrentPos->GetToMove() == WHITE) {
    2071           0 :                             tb->PauseTranslations ();
    2072           0 :                             tb->PrintString (nextColumn);
    2073           0 :                             tb->ResumeTranslations ();
    2074             :                         }
    2075           0 :                         tb->PrintString (endColumn);
    2076           0 :                         tb->PrintString (endTable);
    2077           0 :                         endedColumn = true;
    2078             :                     }
    2079             :                 }
    2080           0 :                 if (IsHtmlFormat()  &&  VarDepth == 0) {
    2081           0 :                     tb->PrintString ("</b>");
    2082             :                 }
    2083           0 :                 if (IsLatexFormat()) {
    2084             :                     // The commented-out code below will print diagrams
    2085             :                     // in variations smaller than game diagrams:
    2086             :                     //if (VarDepth == 0) {
    2087             :                     //    tb->PrintString("\n\\font\\Chess=chess20\n");
    2088             :                     //} else {
    2089             :                     //    tb->PrintString("\n\\font\\Chess=chess10\n");
    2090             :                     //}
    2091           0 :                     tb->PrintString ("\n\\begin{diagram}\n");
    2092             :                 }
    2093           0 :                 MoveForward ();
    2094           0 :                 DString * dstr = new DString;
    2095           0 :                 if (IsHtmlFormat()) {
    2096           0 :                     CurrentPos->DumpHtmlBoard (dstr, HtmlStyle, NULL);
    2097             :                 } else {
    2098           0 :                     CurrentPos->DumpLatexBoard (dstr);
    2099             :                 }
    2100           0 :                 MoveBackup ();
    2101           0 :                 tb->PrintString (dstr->Data());
    2102           0 :                 delete dstr;
    2103           0 :                 if (IsHtmlFormat()  &&  VarDepth == 0) {
    2104           0 :                     tb->PrintString ("<b>");
    2105             :                 }
    2106           0 :                 if (IsLatexFormat()) {
    2107           0 :                     tb->PrintString ("\n\\end{diagram}\n");
    2108             :                 }
    2109           0 :                 printMoveNum = true;
    2110             :             }
    2111             : 
    2112     1561972 :             if (!m->comment.empty() && ! CommentEmpty(m->comment.c_str()) ) {
    2113      446938 :                 if (!inComment && IsPlainFormat()  &&
    2114      223469 :                     (PgnStyle & PGN_STYLE_NO_NULL_MOVES)) {
    2115             :                     // If this move has no variations, but the next move
    2116             :                     // is a null move, enter inComment mode:
    2117           0 :                     if (m->next->isNull()  &&
    2118           0 :                           ((!(PgnStyle & PGN_STYLE_VARS))  ||
    2119           0 :                             (CurrentMove->next->numVariations == 0))) {
    2120           0 :                         inComment = true;
    2121           0 :                         tb->PrintString(preCommentStr);
    2122           0 :                         preCommentStr = "";
    2123           0 :                         postCommentStr = "";
    2124             :                     }
    2125             :                 }
    2126             : 
    2127             : /* Code commented to remove extra lines
    2128             :                 if ((PgnStyle & PGN_STYLE_COLUMN)  &&  VarDepth == 0) {
    2129             :                        if (! endedColumn) {
    2130             :                            if (CurrentPos->GetToMove() == WHITE) {
    2131             :                                tb->PauseTranslations ();
    2132             :                                tb->PrintString (nextColumn);
    2133             :                                tb->ResumeTranslations ();
    2134             :                            }
    2135             :                            tb->PrintString (endColumn);
    2136             :                            tb->PrintString (endTable);
    2137             :                            endedColumn = true;
    2138             :                        }
    2139             :                 }
    2140             : */
    2141      223469 :                 if (IsHtmlFormat()  &&  VarDepth == 0) {
    2142           0 :                     tb->PrintString ("</b><dl><dd>");
    2143             :                 }
    2144      223469 :                 if ((PgnStyle & PGN_STYLE_INDENT_COMMENTS) && VarDepth == 0) {
    2145           0 :                     if (IsColorFormat()) {
    2146           0 :                         tb->PrintString ("<br><ip1>");
    2147             :                     } else {
    2148           0 :                         tb->SetIndent (tb->GetIndent() + 4); tb->Indent();
    2149             :                     }
    2150             :                 }
    2151             : 
    2152      223469 :                 WriteComment (tb, preCommentStr, m->comment.c_str(), postCommentStr);
    2153             : 
    2154      223469 :                 if ((PgnStyle & PGN_STYLE_INDENT_COMMENTS) && VarDepth == 0) {
    2155           0 :                     if (IsColorFormat()) {
    2156           0 :                         tb->PrintString ("</ip1><br>");
    2157           0 :                         commentLine = true;
    2158             :                     } else {
    2159           0 :                         tb->SetIndent (tb->GetIndent() - 4); tb->Indent();
    2160             :                     }
    2161             :                 } else {
    2162      223469 :                     tb->PrintSpace();
    2163             :                 }
    2164      223469 :                 if (printDiagrams  &&  strIsPrefix ("#", m->comment.c_str())) {
    2165           0 :                     if (IsLatexFormat()) {
    2166           0 :                         tb->PrintString ("\n\\begin{diagram}\n");
    2167             :                     }
    2168           0 :                     MoveForward ();
    2169           0 :                     DString * dstr = new DString;
    2170           0 :                     if (IsHtmlFormat()) {
    2171           0 :                         CurrentPos->DumpHtmlBoard (dstr, HtmlStyle, NULL);
    2172             :                     } else {
    2173           0 :                         CurrentPos->DumpLatexBoard (dstr);
    2174             :                     }
    2175           0 :                     MoveBackup ();
    2176           0 :                     tb->PrintString (dstr->Data());
    2177           0 :                     if (IsLatexFormat()) {
    2178           0 :                         tb->PrintString ("\n\\end{diagram}\n");
    2179             :                     }
    2180           0 :                     delete dstr;
    2181             :                 }
    2182      223469 :                 if (IsHtmlFormat() && VarDepth == 0) {
    2183           0 :                     tb->PrintString ("</dl><b>");
    2184             :                 }
    2185      223469 :                 printMoveNum = true;
    2186             :             }
    2187             :         } else {
    2188           0 :             tb->PrintSpace();
    2189             :         }
    2190             : 
    2191             :         // Print any variations if the style indicates:
    2192     1561972 :         if ((PgnStyle & PGN_STYLE_VARS)  &&  (m->numVariations > 0)) {
    2193      308438 :             if ((PgnStyle & PGN_STYLE_COLUMN)  &&  VarDepth == 0) {
    2194           0 :                 if (! endedColumn) {
    2195           0 :                     if (CurrentPos->GetToMove() == WHITE) {
    2196           0 :                         tb->PauseTranslations ();
    2197           0 :                         tb->PrintString (nextColumn);
    2198           0 :                         tb->ResumeTranslations ();
    2199             :                     }
    2200             :                     // Doesn't seem wanted!! S.A (see a few lines below)
    2201             :                     // tb->PrintString (endColumn);
    2202           0 :                     tb->PrintString (endTable);
    2203           0 :                     endedColumn = true;
    2204             :                 }
    2205             :             }
    2206      308438 :             if (IsColorFormat()  &&  VarDepth == 0) { tb->PrintString ("<var>"); }
    2207             :             // Doesn't indent first var in column mode properly 
    2208             :             // if including !(PgnStyle & PGN_STYLE_COLUMN) here.
    2209             :             // But as-is, depth 3 vars don't indent in COLUMN mode (bug)
    2210      308438 :             if ((PgnStyle & PGN_STYLE_INDENT_VARS) && IsColorFormat()) {
    2211           0 :                 if ( !commentLine ) {
    2212           0 :                     tb->PrintString ("<br>");
    2213             :                 }
    2214             :             }
    2215      616916 :             for (uint i=0; i < m->numVariations; i++) {
    2216      308478 :                 if (PgnStyle & PGN_STYLE_INDENT_VARS) {
    2217           0 :                     if (IsColorFormat()) {
    2218           0 :                         switch (VarDepth) {
    2219           0 :                             case 0: tb->PrintString ("<ip1>"); break;
    2220           0 :                             case 1: tb->PrintString ("<ip2>"); break;
    2221           0 :                             case 2: tb->PrintString ("<ip3>"); break;
    2222           0 :                             case 3: tb->PrintString ("<ip4>"); break;
    2223             :                         }
    2224             :                     } else {
    2225           0 :                         tb->SetIndent (tb->GetIndent() + 4); tb->Indent();
    2226             :                     }
    2227             :                 }
    2228      308478 :                 if (IsHtmlFormat()) {
    2229           0 :                     if (VarDepth == 0) { tb->PrintString ("</b><dl><dd>"); }
    2230             :                 }
    2231      308478 :                 if (IsLatexFormat()  &&  VarDepth == 0) {
    2232           0 :                     if (PgnStyle & PGN_STYLE_INDENT_VARS) {
    2233           0 :                         tb->PrintLine ("\\begin{variation}");
    2234             :                     } else {
    2235           0 :                         tb->PrintString ("{\\rm ");
    2236             :                     }
    2237             :                 }
    2238      308478 :                 if (IsColorFormat()) { tb->PrintString ("<blue>"); }
    2239             : 
    2240             :                 // Note tabs in column mode don't work after this VarDepth>1 for some reason
    2241             :                 // this VarDepth check is redundant i think 
    2242      308478 :                 if (!IsLatexFormat()  ||  VarDepth != 0) {
    2243      308478 :                     tb->PrintChar ('(');
    2244             :                 }
    2245             : 
    2246      308478 :                 MoveIntoVariation (i);
    2247      308478 :                 NumMovesPrinted++;
    2248      308478 :                 tb->PrintSpace();
    2249             : 
    2250             :                 // Recursively print the variation:
    2251      308478 :                 WriteMoveList (tb, oldCurrentMove, true, inComment);
    2252             : 
    2253      308478 :                 MoveExitVariation();
    2254      308478 :                 if (!IsLatexFormat()  ||  VarDepth != 0) {
    2255      308478 :                     tb->PrintChar (')');
    2256             :                 }
    2257      308478 :                 if (IsColorFormat()) { tb->PrintString ("<blue>"); }
    2258      308478 :                 if (IsHtmlFormat()) {
    2259           0 :                     if (VarDepth == 0) { tb->PrintString ("</dl><b>"); }
    2260             :                 }
    2261      308478 :                 if (IsLatexFormat()  &&  VarDepth == 0) {
    2262           0 :                     if (PgnStyle & PGN_STYLE_INDENT_VARS) {
    2263           0 :                         tb->PrintLine ("\\end{variation}");
    2264             :                     } else {
    2265           0 :                         tb->PrintString ("}");
    2266             :                     }
    2267             :                 }
    2268      308478 :                 if (PgnStyle & PGN_STYLE_INDENT_VARS) {
    2269           0 :                     if (IsColorFormat()) {
    2270           0 :                         switch (VarDepth) {
    2271           0 :                             case 0: tb->PrintString ("</ip1><br>"); break;
    2272           0 :                             case 1: tb->PrintString ("</ip2><br>"); break;
    2273           0 :                             case 2: tb->PrintString ("</ip3><br>"); break;
    2274           0 :                             case 3: tb->PrintString ("</ip4><br>"); break;
    2275             :                         }
    2276             :                     } else {
    2277           0 :                         tb->SetIndent (tb->GetIndent() - 4); tb->Indent();
    2278             :                     }
    2279      308478 :                 } else { tb->PrintSpace(); }
    2280      308478 :                 printMoveNum = true;
    2281             :             }
    2282      308438 :             if (IsColorFormat()  &&  VarDepth == 0) {
    2283           0 :                 tb->PrintString ("</var>");
    2284             :             }
    2285             :         }
    2286     1561972 :         if ((PgnStyle & PGN_STYLE_COLUMN)  &&  VarDepth == 0) {
    2287           0 :             if (endedColumn) { tb->PrintString(startTable); }
    2288           0 :             if (!endedColumn  &&  CurrentPos->GetToMove() == BLACK) {
    2289           0 :                 tb->PrintString (endColumn);
    2290           0 :                 endedColumn = true;
    2291             :             }
    2292             :         }
    2293     1561972 :         MoveForward();
    2294             :     }
    2295      310515 :     if (inComment) { tb->PrintString ("}"); }
    2296      310515 :     if (IsHtmlFormat()  &&  VarDepth == 0) { tb->PrintString ("</b>"); }
    2297      310515 :     if ((PgnStyle & PGN_STYLE_COLUMN)  &&  VarDepth == 0) {
    2298           0 :         tb->PrintString(endTable);
    2299             :     }
    2300      310515 :     return OK;
    2301             : }
    2302             : 
    2303             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2304             : // Game::WritePGN():
    2305             : //      Write a game in PGN to a textbuffer.
    2306             : //
    2307        2037 : errorT Game::WritePGN(TextBuffer* tb) {
    2308             :     char temp[256];
    2309             :     char dateStr [20];
    2310        2037 :     const char * newline = "\n";
    2311        2037 :     tb->NewlinesToSpaces (false);
    2312        2037 :     if (IsHtmlFormat()) { newline = "<br>\n"; }
    2313        2037 :     if (IsLatexFormat()) {
    2314           0 :         newline = "\\\\\n";
    2315           0 :         tb->AddTranslation ('#', "\\#");
    2316           0 :         tb->AddTranslation ('%', "\\%");
    2317           0 :         tb->AddTranslation ('&', "\\&");
    2318           0 :         tb->AddTranslation ('<', "$<$");
    2319           0 :         tb->AddTranslation ('>', "$>$");
    2320           0 :         tb->AddTranslation ('_', "\\_");
    2321             :         // tb->AddTranslation ('[', "$[$");
    2322             :         // tb->AddTranslation (']', "$]$");
    2323             :     }
    2324        2037 :     if (IsColorFormat()) {
    2325           0 :         newline = "<br>";
    2326             :     }
    2327             : 
    2328        2037 :     if (PgnStyle & PGN_STYLE_COLUMN) {
    2329           0 :         PgnStyle |= PGN_STYLE_INDENT_COMMENTS;
    2330           0 :         PgnStyle |= PGN_STYLE_INDENT_VARS;
    2331             :     }
    2332             : 
    2333             :     // First: is there a pre-game comment? If so, print it:
    2334             : //    if (FirstMove->comment != NULL && (PgnStyle & PGN_STYLE_COMMENTS)
    2335             : //        &&  ! strIsAllWhitespace (FirstMove->comment)) {
    2336             : //        tb->AddTranslation ('\n', newline);
    2337             : //        char * s = FirstMove->comment;
    2338             : //        if (PgnStyle & PGN_STYLE_STRIP_MARKS) {
    2339             : //            s = strDuplicate (FirstMove->comment);
    2340             : //            strTrimMarkCodes (s);
    2341             : //        }
    2342             : //        if (IsColorFormat()) {
    2343             : //            sprintf (temp, "<c_%u>", NumMovesPrinted);
    2344             : //            tb->PrintString (temp);
    2345             : //            tb->AddTranslation ('<', "<lt>");
    2346             : //            tb->AddTranslation ('>', "<gt>");
    2347             : //            tb->PrintString (s);
    2348             : //            tb->ClearTranslation ('<');
    2349             : //            tb->ClearTranslation ('>');
    2350             : //            tb->PrintLine ("</c>");
    2351             : //        } else {
    2352             : //            tb->PrintLine (s);
    2353             : //        }
    2354             : //        if (PgnStyle & PGN_STYLE_STRIP_MARKS) { delete[] s; }
    2355             : //        tb->ClearTranslation ('\n');
    2356             : //        tb->NewLine();
    2357             : //    }
    2358             : 
    2359        2037 :     date_DecodeToString (Date, dateStr);
    2360        2037 :     if (IsHtmlFormat()) { tb->PrintLine("<p><b>"); }
    2361        2037 :     if (IsLatexFormat()) { tb->PrintLine ("{\\bf"); }
    2362             : 
    2363             : //    if (IsColorFormat()) {
    2364             : //        tb->AddTranslation ('<', "<lt>");
    2365             : //        tb->AddTranslation ('>', "<gt>");
    2366             : //    }
    2367             : 
    2368        2037 :     if (PgnStyle & PGN_STYLE_SHORT_HEADER) {
    2369             :         // Print tags in short, 3-line format:
    2370             : 
    2371             :         //if (IsHtmlFormat()) { tb->PrintString ("<font size=+1>"); }
    2372           0 :         if (IsLatexFormat()) { tb->PrintString ("$\\circ$ "); }
    2373           0 :         if (PgnFormat==PGN_FORMAT_Color) {tb->PrintString ("<tag>"); }
    2374           0 :         tb->PrintString (GetWhiteStr());
    2375           0 :         if (WhiteElo > 0) {
    2376           0 :             sprintf (temp, "  (%u)", WhiteElo);
    2377           0 :             tb->PrintString (temp);
    2378             :         }
    2379           0 :         switch (PgnFormat) {
    2380           0 :         case PGN_FORMAT_HTML:
    2381           0 :             tb->PrintString (" &nbsp;&nbsp; -- &nbsp;&nbsp; ");
    2382           0 :             break;
    2383           0 :         case PGN_FORMAT_LaTeX:
    2384           0 :             tb->PrintString (newline);
    2385           0 :             tb->PrintString ("$\\bullet$ ");
    2386           0 :             break;
    2387           0 :         default:
    2388           0 :             tb->PrintString ("   --   ");
    2389           0 :             break;
    2390             :         }
    2391           0 :         tb->PrintString (GetBlackStr());
    2392           0 :         if (BlackElo > 0) {
    2393           0 :             sprintf (temp, "  (%u)", BlackElo);
    2394           0 :             tb->PrintString (temp);
    2395             :         }
    2396             :         //if (IsHtmlFormat()) { tb->PrintString ("</font>"); }
    2397           0 :         tb->PrintString (newline);
    2398             : 
    2399           0 :         tb->PrintString (GetEventStr());
    2400           0 :         if (!RoundStr.empty() && RoundStr != "?") {
    2401           0 :             tb->PrintString (IsHtmlFormat() ? " &nbsp;(" : " (");
    2402           0 :             tb->PrintString (GetRoundStr());
    2403           0 :             tb->PrintString (")");
    2404             :         }
    2405           0 :         tb->PrintString (IsHtmlFormat() ? "&nbsp;&nbsp; " : "  ");
    2406           0 :         if (IsLatexFormat()) { tb->PrintString (newline); }
    2407           0 :         if (!SiteStr.empty() && SiteStr != "?") {
    2408           0 :             tb->PrintString (GetSiteStr());
    2409           0 :             tb->PrintString (newline);
    2410             :         }
    2411             : 
    2412             :         // Remove ".??" or ".??.??" from end of dateStr, then print it:
    2413           0 :         if (dateStr[4] == '.'  &&  dateStr[5] == '?') { dateStr[4] = 0; }
    2414           0 :         if (dateStr[7] == '.'  &&  dateStr[8] == '?') { dateStr[7] = 0; }
    2415           0 :         tb->PrintString (dateStr);
    2416             : 
    2417             :         // Print ECO code:
    2418           0 :         tb->PrintString (IsHtmlFormat() ? " &nbsp; &nbsp; " : "  ");
    2419           0 :         if (IsLatexFormat()) { tb->PrintString ("\\hfill "); }
    2420           0 :         tb->PrintString (RESULT_LONGSTR[Result]);
    2421           0 :         if (EcoCode != 0) {
    2422           0 :             tb->PrintString (IsHtmlFormat() ? " &nbsp; &nbsp; " : "  ");
    2423           0 :             if (IsLatexFormat()) { tb->PrintString ("\\hfill "); }
    2424             :             ecoStringT ecoStr;
    2425           0 :             eco_ToExtendedString (EcoCode, ecoStr);
    2426           0 :             tb->PrintString (ecoStr);
    2427             :         }
    2428           0 :         auto annotator = FindExtraTag("Annotator");
    2429           0 :         if (annotator != NULL) {
    2430           0 :             sprintf(temp, " (%s)", annotator);
    2431           0 :             tb->PrintString(temp);
    2432             :         }
    2433             : 
    2434           0 :         tb->PrintString (newline);
    2435           0 :         if (PgnFormat==PGN_FORMAT_Color) {tb->PrintString ("</tag>"); }
    2436             : 
    2437             :         // Print FEN if non-standard start:
    2438           0 :         if (StartPos) {
    2439           0 :             if (IsLatexFormat()) {
    2440           0 :                 tb->PrintString ("\n\\begin{diagram}\n");
    2441           0 :                 DString dstr;
    2442           0 :                 StartPos->DumpLatexBoard (&dstr);
    2443           0 :                 tb->PrintString (dstr.Data());
    2444           0 :                 tb->PrintString ("\n\\end{diagram}\n");
    2445           0 :             } else if (IsHtmlFormat()) {
    2446           0 :                 DString dstr;
    2447           0 :                 StartPos->DumpHtmlBoard (&dstr, HtmlStyle, NULL);
    2448           0 :                 tb->PrintString (dstr.Data());
    2449             :             } else {
    2450           0 :                 StartPos->PrintFEN(std::copy_n("Position: ", 10, temp),
    2451             :                                    FEN_ALL_FIELDS);
    2452           0 :                 std::strcat(temp, newline);
    2453           0 :                 tb->PrintString (temp);
    2454             :             }
    2455             :         }
    2456             :     } else {
    2457             :         // Print tags in standard PGN format, one per line:
    2458             :         // Note: we want no line-wrapping when printing PGN tags
    2459             :         // so set it to a huge value for now:
    2460        2037 :         uint wrapColumn = tb->GetWrapColumn();
    2461        2037 :         tb->SetWrapColumn (99999);
    2462        2037 :         if (IsColorFormat()) { tb->PrintString ("<tag>"); }
    2463        2037 :         sprintf (temp, "[Event \"%s\"]%s", GetEventStr(), newline);
    2464        2037 :         tb->PrintString (temp);
    2465        2037 :         sprintf (temp, "[Site \"%s\"]%s", GetSiteStr(), newline);
    2466        2037 :         tb->PrintString (temp);
    2467        2037 :         sprintf (temp, "[Date \"%s\"]%s", dateStr, newline);
    2468        2037 :         tb->PrintString (temp);
    2469        2037 :         sprintf (temp, "[Round \"%s\"]%s", GetRoundStr(), newline);
    2470        2037 :         tb->PrintString (temp);
    2471        2037 :         sprintf (temp, "[White \"%s\"]%s", GetWhiteStr(), newline);
    2472        2037 :         tb->PrintString (temp);
    2473        2037 :         sprintf (temp, "[Black \"%s\"]%s", GetBlackStr(), newline);
    2474        2037 :         tb->PrintString (temp);
    2475        2037 :         sprintf (temp, "[Result \"%s\"]%s", RESULT_LONGSTR[Result], newline);
    2476        2037 :         tb->PrintString (temp);
    2477             : 
    2478             :         // Print all tags, not just the standard seven, if applicable:
    2479        2037 :         if (PgnStyle & PGN_STYLE_TAGS) {
    2480        2037 :             if (WhiteElo > 0) {
    2481          60 :                 sprintf (temp, "[White%s \"%u\"]%s",
    2482          60 :                          ratingTypeNames [WhiteRatingType], WhiteElo, newline);
    2483          30 :                 tb->PrintString (temp);
    2484             :             }
    2485        2037 :             if (BlackElo > 0) {
    2486          60 :                 sprintf (temp, "[Black%s \"%u\"]%s",
    2487          60 :                          ratingTypeNames [BlackRatingType], BlackElo, newline);
    2488          30 :                 tb->PrintString (temp);
    2489             :             }
    2490        2037 :             if (EcoCode != 0) {
    2491             :                 ecoStringT ecoStr;
    2492          30 :                 eco_ToExtendedString (EcoCode, ecoStr);
    2493          30 :                 sprintf (temp, "[ECO \"%s\"]%s", ecoStr, newline);
    2494          30 :                 tb->PrintString (temp);
    2495             :             }
    2496        2037 :             if (EventDate != ZERO_DATE) {
    2497             :                 char edateStr [20];
    2498          30 :                 date_DecodeToString (EventDate, edateStr);
    2499          30 :                 sprintf (temp, "[EventDate \"%s\"]%s", edateStr, newline);
    2500          30 :                 tb->PrintString (temp);
    2501             :             }
    2502             : 
    2503        2037 :             if (PgnStyle & PGN_STYLE_SCIDFLAGS  &&  *ScidFlags != 0) {
    2504           0 :                 sprintf (temp, "[ScidFlags \"%s\"]%s", ScidFlags, newline);
    2505           0 :                 tb->PrintString (temp);
    2506             :             }
    2507             : 
    2508             :             // Now print other tags
    2509        2761 :             for (auto& e : extraTags_) {
    2510         724 :                 sprintf(temp, "[%s \"%s\"]%s", e.first.c_str(),
    2511             :                         e.second.c_str(), newline);
    2512         724 :                 tb->PrintString(temp);
    2513             :             }
    2514             :         }
    2515             :         // Finally, write the FEN tag if necessary:
    2516        2037 :         if (StartPos) {
    2517           0 :             StartPos->PrintFEN(std::copy_n("[FEN \"", 6, temp), FEN_ALL_FIELDS);
    2518           0 :             auto it_end = std::copy_n("\"]", 2, temp + std::strlen(temp));
    2519           0 :             std::strcpy(it_end, newline);
    2520           0 :             tb->PrintString (temp);
    2521             :         }
    2522        2037 :         if (IsColorFormat()) { tb->PrintString ("</tag>"); }
    2523             :         // Now restore the linewrap column:
    2524        2037 :         tb->SetWrapColumn (wrapColumn);
    2525             :     }
    2526             : 
    2527             : //    if (IsColorFormat()) {
    2528             : //        tb->ClearTranslation ('<');
    2529             : //        tb->ClearTranslation ('>');
    2530             : //    }
    2531             : 
    2532        2037 :     if (IsHtmlFormat()) { tb->PrintLine("</b></p>"); }
    2533        2037 :     if (IsLatexFormat()) {
    2534           0 :         tb->PrintLine ("}\n\\begin{chess}{\\bf ");
    2535             :     } else {
    2536        2037 :         tb->PrintString (newline);
    2537             :     }
    2538             : 
    2539        2037 :     MoveToPly(0);
    2540             : 
    2541        2037 :     if (IsHtmlFormat()) { tb->PrintString ("<p>"); }
    2542        2037 :     NumMovesPrinted = 1;
    2543        2037 :     WriteMoveList(tb, CurrentMove, true, false);
    2544        2037 :     if (IsHtmlFormat()) { tb->PrintString ("<b>"); }
    2545        2037 :     if (IsLatexFormat()) { tb->PrintString ("\n}\\end{chess}\n{\\bf "); }
    2546        2037 :     if (IsColorFormat()) { tb->PrintString ("<tag>"); }
    2547        2037 :     tb->PrintWord (RESULT_LONGSTR [Result]);
    2548        2037 :     if (IsLatexFormat()) {
    2549           0 :         tb->PrintString ("}\n\\begin{center} \\hrule \\end{center}");
    2550             :     }
    2551        2037 :     if (IsHtmlFormat()) { tb->PrintString ("</b><hr></p>"); }
    2552        2037 :     if (IsColorFormat()) { tb->PrintString ("</tag>"); }
    2553        2037 :     tb->NewLine();
    2554             : 
    2555        2037 :     return OK;
    2556             : }
    2557             : 
    2558             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2559             : // Game::WriteToPGN():
    2560             : //      Print the entire game.
    2561             : //
    2562             : std::pair<const char*, unsigned>
    2563        2037 : Game::WriteToPGN(uint lineWidth, bool NewLineAtEnd, bool newLineToSpaces)
    2564             : {
    2565        2037 :     static TextBuffer tbuf;
    2566             : 
    2567        2037 :     auto location = currentLocation();
    2568        2037 :     tbuf.Empty();
    2569        2037 :     tbuf.SetWrapColumn(lineWidth ? lineWidth : tbuf.GetBufferSize());
    2570        2037 :     tbuf.NewlinesToSpaces(newLineToSpaces);
    2571        2037 :     WritePGN(&tbuf);
    2572        2037 :     if (NewLineAtEnd) tbuf.NewLine();
    2573        2037 :     restoreLocation(location);
    2574        2037 :     return std::make_pair(tbuf.GetBuffer(), tbuf.GetByteCount());
    2575             : }
    2576             : 
    2577             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2578             : // Game::LoadStandardTags():
    2579             : //      Sets the standard tag values for this game, given an
    2580             : //      index file entry and a namebase that stores the
    2581             : //      player/site/event/round names.
    2582             : //
    2583             : errorT
    2584        1000 : Game::LoadStandardTags (const IndexEntry* ie, const NameBase* nb)
    2585             : {
    2586        1000 :     ASSERT (ie != NULL  &&  nb != NULL);
    2587        1000 :     SetEventStr (ie->GetEventName (nb));
    2588        1000 :     SetSiteStr (ie->GetSiteName (nb));
    2589        1000 :     SetWhiteStr (ie->GetWhiteName (nb));
    2590        1000 :     SetBlackStr (ie->GetBlackName (nb));
    2591        1000 :     SetRoundStr (ie->GetRoundName (nb));
    2592        1000 :     SetDate (ie->GetDate());
    2593        1000 :     SetEventDate (ie->GetEventDate());
    2594        1000 :     SetWhiteElo (ie->GetWhiteElo());
    2595        1000 :     SetBlackElo (ie->GetBlackElo());
    2596        1000 :     WhiteEstimateElo = nb->GetElo (ie->GetWhite());
    2597        1000 :     BlackEstimateElo = nb->GetElo (ie->GetBlack());
    2598        1000 :     SetWhiteRatingType (ie->GetWhiteRatingType());
    2599        1000 :     SetBlackRatingType (ie->GetBlackRatingType());
    2600        1000 :     SetResult (ie->GetResult());
    2601        1000 :     SetEco (ie->GetEcoCode());
    2602        1000 :     ie->GetFlagStr (ScidFlags, NULL);
    2603        1000 :     return OK;
    2604             : }
    2605             : 
    2606             : eloT
    2607           0 : Game::GetAverageElo ()
    2608             : {
    2609           0 :     eloT white = WhiteElo;
    2610           0 :     eloT black = BlackElo;
    2611           0 :     if (white == 0) { white = WhiteEstimateElo; }
    2612           0 :     if (black == 0) { black = BlackEstimateElo; }
    2613           0 :     return (white + black) / 2;
    2614             : }
    2615             : 
    2616             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2617             : // makeMoveByte(): inline routine used for encoding most moves.
    2618             : //
    2619             : static inline byte
    2620     9497630 : makeMoveByte (byte pieceNum, byte value)
    2621             : {
    2622     9497630 :     ASSERT (pieceNum <= 15  &&  value <= 15);
    2623     9497630 :     return (byte)((pieceNum & 15) << 4)  |  (byte)(value & 15);
    2624             : }
    2625             : 
    2626             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2627             : // encodeKing(): encoding of King moves.
    2628             : //
    2629             : static inline void
    2630     3489963 : encodeKing (ByteBuffer * buf, simpleMoveT * sm)
    2631             : {
    2632             :     // Valid King difference-from-old-square values are:
    2633             :     // -9, -8, -7, -1, 1, 7, 8, 9, and -2 and 2 for castling.
    2634             :     // To convert this to a val in the range [1-10], we add 9 and
    2635             :     // then look up the val[] table.
    2636             :     // Coded values 1-8 are one-square moves; 9 and 10 are Castling.
    2637             : 
    2638     3489963 :     ASSERT(sm->pieceNum == 0);  // Kings MUST be piece Number zero.
    2639     3489963 :     int diff = (int) sm->to - (int) sm->from;
    2640             :     static const byte val[] = {
    2641             :     /* -9 -8 -7 -6 -5 -4 -3 -2 -1  0  1   2  3  4  5  6  7  8  9 */
    2642             :         1, 2, 3, 0, 0, 0, 0, 9, 4, 0, 5, 10, 0, 0, 0, 0, 6, 7, 8
    2643             :     };
    2644             : 
    2645             :     // If target square is the from square, it is the null move, which
    2646             :     // is represented as a king move to its own square and is encoded
    2647             :     // as the byte value zero.
    2648     3489963 :     if (sm->to == sm->from) {
    2649           0 :         buf->PutByte (makeMoveByte (0, 0));
    2650           0 :         return;
    2651             :     }
    2652             : 
    2653             :     // Verify we have a valid King move:
    2654     3489963 :     ASSERT(diff >= -9  &&  diff <= 9  &&  val[diff+9] != 0);
    2655     3489963 :     buf->PutByte (makeMoveByte (0, val [diff + 9]));
    2656             : }
    2657             : 
    2658             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2659             : // decodeKing(): decoding of King moves.
    2660             : //
    2661             : static inline errorT
    2662      256038 : decodeKing (byte val, simpleMoveT * sm)
    2663             : {
    2664             :     static const int sqdiff[] = {
    2665             :         0, -9, -8, -7, -1, 1, 7, 8, 9, -2, 2
    2666             :     };
    2667             : 
    2668      256038 :     if (val == 0) {
    2669           0 :       sm->to = sm->from;  // Null move
    2670           0 :         return OK;
    2671             :     }
    2672             : 
    2673      256038 :     if (val < 1  ||  val > 10) { return ERROR_Decode; }
    2674      256038 :     sm->to = sm->from + sqdiff[val];
    2675      256038 :     return OK;
    2676             : }
    2677             : 
    2678             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2679             : // encodeKnight(): encoding Knight moves.
    2680             : //
    2681             : static inline void
    2682     1028594 : encodeKnight (ByteBuffer * buf, simpleMoveT * sm)
    2683             : {
    2684             :     // Valid Knight difference-from-old-square values are:
    2685             :     // -17, -15, -10, -6, 6, 10, 15, 17.
    2686             :     // To convert this to a value in the range [1-8], we add 17 to
    2687             :     // the difference and then look up the val[] table.
    2688             : 
    2689     1028594 :     int diff = (int) sm->to - (int) sm->from;
    2690             :     static const byte val[] = {
    2691             :     /* -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1  0 */
    2692             :         1,  0,  2,  0,  0,  0,  0,  3,  0, 0, 0, 4, 0, 0, 0, 0, 0, 0,
    2693             : 
    2694             :     /*  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 */
    2695             :         0, 0, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 0, 7, 0, 8
    2696             :     };
    2697             : 
    2698             :     // Verify we have a valid knight move:
    2699     1028594 :     ASSERT (diff >= -17  &&  diff <= 17  &&  val[diff + 17] != 0);
    2700     1028594 :     buf->PutByte (makeMoveByte (sm->pieceNum, val [diff + 17]));
    2701     1028594 : }
    2702             : 
    2703             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2704             : // decodeKnight(): decoding Knight moves.
    2705             : //
    2706             : static inline errorT
    2707      101250 : decodeKnight (byte val, simpleMoveT * sm)
    2708             : {
    2709             :     static const int sqdiff[] = {
    2710             :         0, -17, -15, -10, -6, 6, 10, 15, 17
    2711             :     };
    2712      101250 :     if (val < 1  ||  val > 8) { return ERROR_Decode; }
    2713      101250 :     sm->to = sm->from + sqdiff[val];
    2714      101250 :     return OK;
    2715             : }
    2716             : 
    2717             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2718             : // encodeRook(): encoding rook moves.
    2719             : //
    2720             : static inline void
    2721     1735752 : encodeRook (ByteBuffer * buf, simpleMoveT * sm)
    2722             : {
    2723             :     // Valid Rook moves are to same rank, OR to same fyle.
    2724             :     // We encode the 8 squares on the same rank 0-8, and the 8
    2725             :     // squares on the same fyle 9-15. This means that for any particular
    2726             :     // rook move, two of the values in the range [0-15] will be
    2727             :     // meaningless, as they will represent the from-square.
    2728             : 
    2729     1735752 :     ASSERT (sm->from <= H8  &&  sm->to <= H8);
    2730             :     byte val;
    2731             : 
    2732             :     // Check if the two squares share the same rank:
    2733     1735752 :     if (square_Rank(sm->from) == square_Rank(sm->to)) {
    2734      911338 :         val = square_Fyle(sm->to);
    2735             :     } else {
    2736      824414 :         val = 8 + square_Rank(sm->to);
    2737             :     }
    2738     1735752 :     buf->PutByte (makeMoveByte (sm->pieceNum, val));
    2739     1735752 : }
    2740             : 
    2741             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2742             : // decodeRook(): decoding Rook moves.
    2743             : //
    2744             : static inline errorT
    2745      138987 : decodeRook (byte val, simpleMoveT * sm)
    2746             : {
    2747      138987 :     if (val >= 8) {
    2748             :         // This is a move along a Fyle, to a different rank:
    2749       67200 :         sm->to = square_Make (square_Fyle(sm->from), (val - 8));
    2750             :     } else {
    2751       71787 :         sm->to = square_Make (val, square_Rank(sm->from));
    2752             :     }
    2753      138987 :     return OK;
    2754             : }
    2755             : 
    2756             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2757             : // encodeBishop(): encoding Bishop moves.
    2758             : //
    2759             : static inline void
    2760     1621121 : encodeBishop (ByteBuffer * buf, simpleMoveT * sm)
    2761             : {
    2762             :     // We encode a Bishop move as the Fyle moved to, plus
    2763             :     // a one-bit flag to indicate if the direction was
    2764             :     // up-right/down-left or vice versa.
    2765             : 
    2766     1621121 :     ASSERT (sm->to <= H8  &&  sm->from <= H8);
    2767             :     byte val;
    2768     1621121 :     val = square_Fyle(sm->to);
    2769     1621121 :     int rankdiff = (int)square_Rank(sm->to) - (int)square_Rank(sm->from);
    2770     1621121 :     int fylediff = (int)square_Fyle(sm->to) - (int)square_Fyle(sm->from);
    2771             : 
    2772             :     // If (rankdiff * fylediff) is negative, it's up-left/down-right:
    2773     1621121 :     if (rankdiff * fylediff < 0) { val += 8; }
    2774             : 
    2775     1621121 :     buf->PutByte (makeMoveByte (sm->pieceNum, val));
    2776     1621121 : }
    2777             : 
    2778             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2779             : // decodeBishop(): decoding Bishop moves.
    2780             : //
    2781             : static inline errorT
    2782      122008 : decodeBishop (byte val, simpleMoveT * sm)
    2783             : {
    2784      122008 :     byte fyle = (val & 7);
    2785      122008 :     int fylediff = (int)fyle - (int)square_Fyle(sm->from);
    2786      122008 :     if (val >= 8) {
    2787             :         // It is an up-left/down-right direction move.
    2788       60740 :         sm->to = sm->from - 7 * fylediff;
    2789             :     } else {
    2790       61268 :         sm->to = sm->from + 9 * fylediff;
    2791             :     }
    2792      122008 :     if (sm->to > H8) { return ERROR_Decode;}
    2793      122008 :     return OK;
    2794             : }
    2795             : 
    2796             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2797             : // encodeQueen(): encoding Queen moves.
    2798             : //
    2799             : static inline void
    2800      628431 : encodeQueen (ByteBuffer * buf, simpleMoveT * sm)
    2801             : {
    2802             :     // We cannot fit all Queen moves in one byte, so Rooklike moves
    2803             :     // are in one byte (encoded the same way as Rook moves),
    2804             :     // while diagonal moves are in two bytes.
    2805             : 
    2806      628431 :     ASSERT (sm->to <= H8  &&  sm->from <= H8);
    2807             :     byte val;
    2808             : 
    2809      628431 :     if (square_Rank(sm->from) == square_Rank(sm->to)) {
    2810             :         // Rook-horizontal move:
    2811             : 
    2812      174788 :         val = square_Fyle(sm->to);
    2813      174788 :         buf->PutByte (makeMoveByte (sm->pieceNum, val));
    2814             : 
    2815      453643 :     } else if (square_Fyle(sm->from) == square_Fyle(sm->to)) {
    2816             :         // Rook-vertical move:
    2817             : 
    2818      157163 :         val = 8 + square_Rank(sm->to);
    2819      157163 :         buf->PutByte (makeMoveByte (sm->pieceNum, val));
    2820             : 
    2821             :     } else {
    2822             :         // Diagonal move:
    2823      296480 :         ASSERT(std::abs(sm->to / 8 - sm->from / 8) ==
    2824             :                std::abs(sm->to % 8 - sm->from % 8));
    2825             : 
    2826             :         // First, we put a rook-horizontal move to the from square (which
    2827             :         // is illegal of course) to indicate it is NOT a rooklike move:
    2828             : 
    2829      296480 :         val = square_Fyle(sm->from);
    2830      296480 :         buf->PutByte (makeMoveByte (sm->pieceNum, val));
    2831             : 
    2832             :         // Now we put the to-square in the next byte. We add a 64 to it
    2833             :         // to make sure that it cannot clash with the Special tokens (which
    2834             :         // are in the range 0 to 15, since they are special King moves).
    2835             : 
    2836      296480 :         buf->PutByte (sm->to + 64);
    2837             :     }
    2838      628431 : }
    2839             : 
    2840             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2841             : // decodeQueen(): decoding Queen moves.
    2842             : //
    2843             : static inline errorT
    2844       66120 : decodeQueen (ByteBuffer * buf, byte val, simpleMoveT * sm)
    2845             : {
    2846       66120 :     if (val >= 8) {
    2847             :         // Rook-vertical move:
    2848       17482 :         sm->to = square_Make (square_Fyle(sm->from), (val - 8));
    2849             : 
    2850       48638 :     } else if (val != square_Fyle(sm->from)) {
    2851             :         // Rook-horizontal move:
    2852       18943 :         sm->to = square_Make (val, square_Rank(sm->from));
    2853             : 
    2854             :     } else {
    2855             :         // Diagonal move: coded in TWO bytes.
    2856       29695 :         val = buf->GetByte();
    2857       29695 :         if (val < 64  ||  val > 127) { return ERROR_Decode; }
    2858       29695 :         sm->to = val - 64;
    2859             :     }
    2860       66120 :     return OK;
    2861             : }
    2862             : 
    2863             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2864             : // encodePawn(): encoding Pawn moves.
    2865             : //
    2866             : static inline void
    2867      993769 : encodePawn (ByteBuffer * buf, simpleMoveT * sm)
    2868             : {
    2869             :     // Pawn moves require a promotion encoding.
    2870             :     // The pawn moves are:
    2871             :     // 0 = capture-left,
    2872             :     // 1 = forward,
    2873             :     // 2 = capture-right (all no promotion);
    2874             :     //    3/4/5 = 0/1/2 with Queen promo;
    2875             :     //    6/7/8 = 0/1/2 with Rook promo;
    2876             :     //  9/10/11 = 0/1/2 with Bishop promo;
    2877             :     // 12/13/14 = 0/1/2 with Knight promo;
    2878             :     // 15 = forward TWO squares.
    2879             : 
    2880             :     byte val;
    2881      993769 :     int diff = (int)(sm->to) - (int)(sm->from);
    2882             : 
    2883      993769 :     if (diff < 0) { diff = -diff; }
    2884      993769 :     if (diff == 16) { // Move forward two squares
    2885      175996 :         val = 15;
    2886      175996 :         ASSERT (sm->promote == EMPTY);
    2887             : 
    2888             :     } else {
    2889      817773 :         if (diff == 7) { val = 0; }
    2890      738974 :         else if (diff == 8) { val = 1; }
    2891             :         else {  // diff is 9:
    2892       85075 :             ASSERT (diff == 9);
    2893       85075 :             val = 2;
    2894             :         }
    2895      817773 :         if (sm->promote != EMPTY) {
    2896             :             // Handle promotions.
    2897             :             // sm->promote must be Queen=2,Rook=3, Bishop=4 or Knight=5.
    2898             :             // We add 3 for Queen, 6 for Rook, 9 for Bishop, 12 for Knight.
    2899             : 
    2900       55474 :             ASSERT (sm->promote >= QUEEN  &&  sm->promote <= KNIGHT);
    2901       55474 :             val += 3 * ((sm->promote) - 1);
    2902             :         }
    2903             :     }
    2904      993769 :     buf->PutByte (makeMoveByte (sm->pieceNum, val));
    2905      993769 : }
    2906             : 
    2907             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2908             : // decodePawn(): decoding Pawn moves.
    2909             : //
    2910             : static inline errorT
    2911       98648 : decodePawn (byte val, simpleMoveT * sm, colorT toMove)
    2912             : {
    2913             :     static const int
    2914             :     toSquareDiff [16] = {
    2915             :         7,8,9, 7,8,9, 7,8,9, 7,8,9, 7,8,9, 16
    2916             :     };
    2917             : 
    2918             :     static const pieceT
    2919             :     promoPieceFromVal [16] = {
    2920             :         EMPTY,EMPTY,EMPTY,
    2921             :         QUEEN,QUEEN,QUEEN,
    2922             :         ROOK,ROOK,ROOK,
    2923             :         BISHOP,BISHOP,BISHOP,
    2924             :         KNIGHT,KNIGHT,KNIGHT,
    2925             :         EMPTY
    2926             :     };
    2927             : 
    2928       98648 :     if (toMove == WHITE) {
    2929       48574 :         sm->to = sm->from + toSquareDiff[val];
    2930             :     } else {
    2931       50074 :         sm->to = sm->from - toSquareDiff[val];
    2932             :     }
    2933             : 
    2934       98648 :     sm->promote = promoPieceFromVal[val];
    2935             : 
    2936       98648 :     return OK;
    2937             : }
    2938             : 
    2939             : 
    2940             : // Special-move tokens:
    2941             : // Since king-move values 1-10 are taken for actual King moves, only
    2942             : // 11-15 (and zero) are available for non-move information.
    2943             : 
    2944             : #define ENCODE_NAG          11
    2945             : #define ENCODE_COMMENT      12
    2946             : #define ENCODE_START_MARKER 13
    2947             : #define ENCODE_END_MARKER   14
    2948             : #define ENCODE_END_GAME     15
    2949             : 
    2950             : #define ENCODE_FIRST        11
    2951             : #define ENCODE_LAST         15
    2952             : 
    2953             : // The end-game and end-variation tokens could be the same single token,
    2954             : // but having two different tokens allows for detecting corruption, since
    2955             : // a game must end with the end-game token.
    2956             : 
    2957             : 
    2958             : // The inline routine  isSpecialMoveCode() returns true is a byte value
    2959             : // has the value of a special non-move token:
    2960             : inline bool
    2961             : isSpecialMoveCode (byte val)
    2962             : {
    2963             :     return (val <= ENCODE_LAST  &&  val >= ENCODE_FIRST);
    2964             : }
    2965             : 
    2966             : 
    2967             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2968             : // decodeMove():
    2969             : //      Decode a move from a bytebuffer. Assumes the byte val is an
    2970             : //      actual move, not the value of a "special" (non-move) token.
    2971             : //      This function needs to be passed the bytebuffer because some
    2972             : //      moves (only Queen diagonal moves) are encoded in two bytes, so
    2973             : //      it may be necessary to read the next byte as well.
    2974             : //
    2975      783051 : static errorT decodeMove(ByteBuffer* buf, simpleMoveT* sm, byte val,
    2976             :                          const Position* pos) {
    2977      783051 :     const squareT* sqList = pos->GetList(pos->GetToMove());
    2978      783051 :     sm->from = sqList[val >> 4];
    2979      783051 :     if (sm->from > H8) { return ERROR_Decode; }
    2980             : 
    2981      783051 :     const pieceT* board = pos->GetBoard();
    2982      783051 :     sm->movingPiece = board[sm->from];
    2983      783051 :     sm->promote = EMPTY;
    2984             : 
    2985      783051 :     errorT err = OK;
    2986      783051 :     pieceT pt = piece_Type (sm->movingPiece);
    2987      783051 :     switch (pt) {
    2988       98648 :     case PAWN:
    2989       98648 :         err = decodePawn (val & 15, sm, pos->GetToMove());
    2990       98648 :         break;
    2991      101250 :     case KNIGHT:
    2992      101250 :         err = decodeKnight (val & 15, sm);
    2993      101250 :         break;
    2994      138987 :     case ROOK:
    2995      138987 :         err = decodeRook (val & 15, sm);
    2996      138987 :         break;
    2997      122008 :     case BISHOP:
    2998      122008 :         err = decodeBishop (val & 15, sm);
    2999      122008 :         break;
    3000      256038 :     case KING:
    3001      256038 :         err = decodeKing (val & 15, sm);
    3002      256038 :         break;
    3003             :     // For queen moves: Rook-like moves are in 1 byte, diagonals are in 2.
    3004       66120 :     case QUEEN:
    3005       66120 :         err = decodeQueen (buf, val & 15, sm);
    3006       66120 :         break;
    3007           0 :     default:
    3008           0 :         err = ERROR_Decode;
    3009             :     }
    3010      783051 :     return err;
    3011             : }
    3012             : 
    3013             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3014             : // Game::EncodeMove():
    3015             : //  Encode one move and output it to the bytebuffer.
    3016             : //
    3017     9497630 : static void encodeMove(ByteBuffer* buf, moveT* m) {
    3018     9497630 :     simpleMoveT* sm = &(m->moveData);
    3019     9497630 :     pieceT pt = piece_Type(sm->movingPiece);
    3020             : 
    3021             :     typedef void encodeFnType (ByteBuffer *, simpleMoveT *);
    3022             :     static encodeFnType * encodeFn[] = {
    3023             :         NULL         /* 0 */,
    3024             :         encodeKing   /*1=KING*/,
    3025             :         encodeQueen  /*2=QUEEN*/,
    3026             :         encodeRook   /*3=ROOK*/,
    3027             :         encodeBishop /*4=BISHOP*/,
    3028             :         encodeKnight /*5=KNIGHT*/,
    3029             :         encodePawn   /*6=PAWN*/
    3030             :     };
    3031     9497630 :     ASSERT (pt >= KING  &&  pt <= PAWN);
    3032     9497630 :     (encodeFn[pt]) (buf, sm);
    3033     9497630 : }
    3034             : 
    3035             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3036             : // encodeVariation(): Used by Encode() to encode the game's moves.
    3037             : //      Recursive; calls itself to encode subvariations.
    3038             : //
    3039     1908932 : static errorT encodeVariation(ByteBuffer* buf, moveT* m, uint* subVarCount,
    3040             :                               uint* nagCount, uint depth) {
    3041     1908932 :     ASSERT(m != NULL);
    3042             : 
    3043             :     // Check if there is a pre-game or start-of-variation comment:
    3044     1908932 :     if (! m->prev->comment.empty()) {
    3045          59 :         buf->PutByte (ENCODE_COMMENT);
    3046             :     }
    3047             : 
    3048    20904192 :     while (m->marker != END_MARKER) {
    3049     9497630 :         encodeMove (buf, m);
    3050     9498114 :         for (uint i=0; i < (uint) m->nagCount; i++) {
    3051         484 :             buf->PutByte (ENCODE_NAG);
    3052         484 :             buf->PutByte (m->nags[i]);
    3053         484 :             *nagCount += 1;
    3054             :         }
    3055     9497630 :         if (! m->comment.empty()) {
    3056     1390835 :             buf->PutByte (ENCODE_COMMENT);
    3057             :         }
    3058     9497630 :         if (m->numVariations > 0) {
    3059     1898860 :             moveT * subVar = m->varChild;
    3060     3797774 :             for (uint i=0; i < m->numVariations; i++) {
    3061     1898914 :                 *subVarCount += 1;
    3062     1898914 :                 buf->PutByte (ENCODE_START_MARKER);
    3063     1898914 :                 encodeVariation (buf, subVar->next, subVarCount, nagCount, depth+1);
    3064     1898914 :                 subVar = subVar->varChild;
    3065             :             }
    3066             :         }
    3067     9497630 :         m = m->next;
    3068             :     }
    3069             :     // At end, we output the end-variation or end-game token.
    3070     1908932 :     if (depth == 0) {
    3071       10018 :         buf->PutByte (ENCODE_END_GAME);
    3072             :     } else {
    3073     1898914 :         buf->PutByte (ENCODE_END_MARKER);
    3074             :     }
    3075     1908932 :     return buf->Status();
    3076             : }
    3077             : 
    3078             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3079             : // Game::DecodeVariation():
    3080             : //      Decodes the game moves. Recursively decodes subvariations.
    3081             : //
    3082             : errorT
    3083      155499 : Game::DecodeVariation (ByteBuffer * buf, byte flags, uint level)
    3084             : {
    3085             :     simpleMoveT sm;
    3086             :     errorT err;
    3087      155499 :     byte b = buf->GetByte ();
    3088     2255397 :     while (b != ENCODE_END_GAME  &&  b != ENCODE_END_MARKER) {
    3089     1049949 :         switch (b) {
    3090      154481 :         case ENCODE_START_MARKER:
    3091      154481 :             err = AddVariation();
    3092      154481 :             if (err != OK) { return err; }
    3093      154481 :             err = DecodeVariation (buf, flags, level + 1);
    3094      154481 :             if (err != OK) { return err; }
    3095      154481 :             err = MoveExitVariation();
    3096      154481 :             if (err != OK) { return err; }
    3097      154481 :             err = MoveForward();
    3098      154481 :             if (err != OK) { return err; }
    3099      154481 :             break;
    3100             : 
    3101         440 :         case ENCODE_NAG:
    3102         440 :             AddNag (buf->GetByte ());
    3103         440 :             break;
    3104             : 
    3105      111977 :         case ENCODE_COMMENT:
    3106      111977 :             if (flags & GAME_DECODE_COMMENTS) {
    3107             :                 // Mark this comment as needing to be read
    3108      111977 :                 CurrentMove->prev->comment = "*";
    3109             :             }
    3110      111977 :             break;
    3111             : 
    3112      783051 :         default:  // It is a regular move
    3113      783051 :             err = decodeMove(buf, &sm, b, currentPos());
    3114      783051 :             if (err != OK)  { return err; }
    3115      783051 :             AddMove(&sm);
    3116             :         }
    3117             : 
    3118     1049949 :         b = buf->GetByte ();
    3119     1049949 :         if (buf->Status() != OK) { return buf->Status(); }
    3120             :     }
    3121             : 
    3122      155499 :     if (level == 0  &&  b != ENCODE_END_GAME) { return ERROR_Decode; }
    3123      155499 :     if (level > 0  &&  b != ENCODE_END_MARKER) { return ERROR_Decode; }
    3124      155499 :     return buf->Status();
    3125             : }
    3126             : 
    3127             : 
    3128             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3129             : // Common tags are encoded in one byte, as a value over 240.
    3130             : //       This means that the maximum length of a non-common tag is 240
    3131             : //       bytes, and the maximum number of common tags is 15.
    3132             : //
    3133             : constexpr size_t MAX_TAG_LEN = 240;
    3134             : 
    3135             : static const char* commonTags[255 - MAX_TAG_LEN] = {
    3136             :     // 241, 242: Country
    3137             :     "WhiteCountry", "BlackCountry",
    3138             :     // 243: Annotator
    3139             :     "Annotator",
    3140             :     // 244: PlyCount
    3141             :     "PlyCount",
    3142             :     // 245: EventDate (plain text encoding)
    3143             :     "EventDate",
    3144             :     // 246, 247: Opening, Variation
    3145             :     "Opening", "Variation",
    3146             :     // 248-250: Setup and Source
    3147             :     "Setup", "Source", "SetUp",
    3148             :     // 252-254: spare for future use
    3149             :     NULL, NULL, NULL, NULL,
    3150             :     // 255: Reserved for compact EventDate encoding
    3151             :     NULL
    3152             : };
    3153             : 
    3154             : 
    3155             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3156             : // encodeTags():
    3157             : //      Encodes the non-standard tags.
    3158             : //
    3159             : template <typename TCont>
    3160       10018 : errorT encodeTags(ByteBuffer* buf, const TCont& tagList) {
    3161       10036 :     for (auto& tag : tagList) {
    3162          18 :         uint tagnum = 1;
    3163          18 :         const char ** common = commonTags;
    3164         108 :         while (*common != NULL) {
    3165          63 :             if (tag.first == *common) {
    3166          18 :                 buf->PutByte ((byte) MAX_TAG_LEN + tagnum);
    3167          18 :                 break;
    3168             :             } else {
    3169          45 :                 common++;
    3170          45 :                 tagnum++;
    3171             :             }
    3172             :         }
    3173          18 :         if (*common == NULL) {   // This is not a common tag.
    3174           0 :             auto length = std::min(tag.first.length(), MAX_TAG_LEN);
    3175           0 :             buf->PutByte ((byte) length);
    3176           0 :             buf->PutFixedString(tag.first.c_str(), length);
    3177             :         }
    3178             : 
    3179          18 :         auto valueLen = std::min<size_t>(tag.second.length(), 255);
    3180          18 :         buf->PutByte ((byte) valueLen);
    3181          18 :         buf->PutFixedString(tag.second.c_str(), valueLen);
    3182             :     }
    3183       10018 :     buf->PutByte (0);
    3184       10018 :     return buf->Status();
    3185             : }
    3186             : 
    3187             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3188             : // Game::DecodeTags():
    3189             : //      Decodes the non-standard tags of the game.
    3190             : //
    3191             : errorT
    3192        1018 : Game::DecodeTags (ByteBuffer * buf, bool storeTags)
    3193             : {
    3194             :     byte b;
    3195             :     char tag [255];
    3196             :     char value [255];
    3197             : 
    3198        1018 :     b = buf->GetByte ();
    3199        1042 :     while (b != 0  &&  buf->Status() == OK) {
    3200          12 :         if (b == 255) {
    3201             :             // Special binary 3-byte encoding of EventDate:
    3202           0 :             dateT date = 0;
    3203           0 :             b = buf->GetByte(); date = (date << 8) | b;
    3204           0 :             b = buf->GetByte(); date = (date << 8) | b;
    3205           0 :             b = buf->GetByte(); date = (date << 8) | b;
    3206           0 :             SetEventDate (date);
    3207             :             //char dateStr[20];
    3208             :             //date_DecodeToString (date, dateStr);
    3209             :             //if (storeTags) { AddPgnTag ("EventDate", dateStr); }
    3210          12 :         } else if (b > MAX_TAG_LEN) {
    3211             :             // A common tag name, not explicitly stored:
    3212          12 :             char * ctag = (char *) commonTags[b - MAX_TAG_LEN - 1];
    3213          12 :             b = buf->GetByte ();
    3214          12 :             buf->GetFixedString (value, b);
    3215          12 :             value[b] = '\0';
    3216          12 :             if (storeTags) { AddPgnTag (ctag, value); }
    3217             :         } else {
    3218           0 :             buf->GetFixedString (tag, b);
    3219           0 :             tag[b] = '\0';
    3220           0 :             b = buf->GetByte ();
    3221           0 :             buf->GetFixedString (value, b);
    3222           0 :             value[b] = '\0';
    3223           0 :             if (storeTags) { AddPgnTag (tag, value); }
    3224             :         }
    3225          12 :         b = buf->GetByte();
    3226             :     }
    3227        1018 :    return buf->Status();
    3228             : }
    3229             : 
    3230             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3231             : // skipTags():
    3232             : //      Called instead of DecodeTags() to skip over the tags of the
    3233             : //      game when decoding it. Called from DecodeStart() since the
    3234             : //      nonstandard tags are not needed for searches.
    3235             : //
    3236             : static errorT
    3237           0 : skipTags (ByteBuffer * buf)
    3238             : {
    3239             :     byte b;
    3240           0 :     b = buf->GetByte ();
    3241           0 :     while (b != 0  &&  buf->Status() == OK) {
    3242           0 :         if (b == 255) {
    3243             :             // Special 3-byte binary encoding of EventDate:
    3244           0 :             buf->Skip (3);
    3245             :         } else {
    3246           0 :             if (b > MAX_TAG_LEN) {
    3247             :                 // Do nothing.
    3248             :             } else {
    3249           0 :                 buf->Skip (b);
    3250             :             }
    3251           0 :             b = buf->GetByte ();
    3252           0 :             buf->Skip (b);
    3253             :         }
    3254           0 :         b = buf->GetByte();
    3255             :     }
    3256           0 :     return buf->Status ();
    3257             : }
    3258             : 
    3259             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3260             : // encodeComments():
    3261             : //      Encode the comments of the game. Recurses the moves of the game
    3262             : //      and writes the comment whenever a move with a comment is found.
    3263             : //
    3264     1908932 : static errorT encodeComments(ByteBuffer* buf, moveT* m, uint* commentCounter) {
    3265     1908932 :     ASSERT(buf != NULL && m != NULL);
    3266             : 
    3267    24722056 :     while (m->marker != END_MARKER) {
    3268    11406562 :         if (! m->comment.empty()) {
    3269     1390894 :             buf->PutTerminatedString (m->comment.c_str());
    3270     1390894 :             *commentCounter += 1;
    3271             :         }
    3272    11406562 :         if (m->numVariations) {
    3273     1898860 :            moveT * subVar = m->varChild;
    3274     3797774 :             for (uint i=0; i < m->numVariations; i++) {
    3275     1898914 :                 encodeComments (buf, subVar, commentCounter);
    3276     1898914 :                 subVar = subVar->varChild;
    3277             :             }
    3278             :         }
    3279    11406562 :         m = m->next;
    3280             :     }
    3281     1908932 :     return buf->Status();
    3282             : }
    3283             : 
    3284             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3285             : // decodeComments():
    3286             : //      Decodes the comments of the game. When decoding the moves, the
    3287             : //      comment field of each move that has a comment is marked (made
    3288             : //      not empty), so this function recurses the movelist and subvariations
    3289             : //      and allocates each comment to its move.
    3290             : //
    3291      155499 : static errorT decodeComments(ByteBuffer* buf, moveT* m) {
    3292      155499 :     ASSERT (buf != NULL  &&  m != NULL);
    3293             : 
    3294     2032599 :     while (m->marker != END_MARKER) {
    3295      938550 :         if (! m->comment.empty()) {
    3296      111977 :             ASSERT (m->comment == "*");
    3297             :             char * str;
    3298      111977 :             buf->GetTerminatedString(&str);
    3299      111977 :             m->comment = str;
    3300             :         }
    3301             : 
    3302      938550 :         if (m->numVariations) {
    3303      154439 :            moveT * subVar = m->varChild;
    3304      308920 :             for (uint i=0; i < m->numVariations; i++) {
    3305      154481 :                 decodeComments (buf, subVar);
    3306      154481 :                 subVar = subVar->varChild;
    3307             :             }
    3308             :         }
    3309      938550 :         m = m->next;
    3310             :     }
    3311      155499 :     return buf->Status();
    3312             : }
    3313             : 
    3314             : 
    3315             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3316             : // Game::Encode(): Encode the game to a buffer for disk storage.
    3317             : //      If passed a NON-null IndexEntry pointer, it will fill in the
    3318             : //      following fields of that index entry, which are computed as
    3319             : //      the game is encoded:
    3320             : //       -  result, ecoCode, whiteElo, blackElo
    3321             : //       -  promotion flag
    3322             : //       -  nMoves: the number of halfmoves
    3323             : //       -  finalMatSig: the material signature of the final position.
    3324             : //       -  homePawnData: the home pawn change list.
    3325             : //
    3326             : errorT
    3327       10018 : Game::Encode (ByteBuffer * buf, IndexEntry * ie)
    3328             : {
    3329       10018 :     ASSERT (buf != NULL);
    3330             : 
    3331             :     // Make the home pawn change list and update PromotionFlag:
    3332             :     byte homePawnList[9];
    3333       10018 :     auto UnderPromosFlag = MakeHomePawnList(homePawnList);
    3334             : 
    3335       10018 :     buf->Empty();
    3336             :     // First, encode info not already stored in the index
    3337             :     // This will be the non-STR (non-"seven tag roster") PGN tags.
    3338       10018 :     errorT err = encodeTags(buf, GetExtraTags());
    3339       10018 :     if (err != OK) { return err; }
    3340             :     // Now the game flags:
    3341       10018 :     byte flags = 0;
    3342       10018 :     if (HasNonStandardStart()) { flags += 1; }
    3343       10018 :     if (PromotionsFlag)   { flags += 2; }
    3344       10018 :     if (UnderPromosFlag)  { flags += 4; }
    3345       10018 :     buf->PutByte (flags);
    3346             :     // Now encode the startBoard, if there is one.
    3347       10018 :     if (StartPos) {
    3348             :         char tempStr [256];
    3349           0 :         StartPos->PrintFEN (tempStr, FEN_ALL_FIELDS);
    3350           0 :         buf->PutTerminatedString (tempStr);
    3351             :     }
    3352             : 
    3353             :     // Now the movelist:
    3354       10018 :     uint varCount = 0;
    3355       10018 :     uint nagCount = 0;
    3356       10018 :     err = encodeVariation (buf, FirstMove->next, &varCount, &nagCount, 0);
    3357       10018 :     if (err != OK) { return err; }
    3358             : 
    3359             :     // Now do the comments
    3360       10018 :     uint commentCount = 0;
    3361             :     
    3362       10018 :     err = encodeComments (buf, FirstMove, &commentCount);
    3363             : 
    3364             :     // Set the fields in the IndexEntry:
    3365       10018 :     if (ie != NULL) {
    3366        9015 :         ie->SetDate (Date);
    3367        9015 :         ie->SetEventDate (EventDate);
    3368        9015 :         ie->SetResult (Result);
    3369        9015 :         ie->SetEcoCode (EcoCode);
    3370        9015 :         ie->SetWhiteElo (WhiteElo);
    3371        9015 :         ie->SetBlackElo (BlackElo);
    3372        9015 :         ie->SetWhiteRatingType (WhiteRatingType);
    3373        9015 :         ie->SetBlackRatingType (BlackRatingType);
    3374             : 
    3375        9015 :         ie->clearFlags();
    3376        9015 :         ie->SetStartFlag (HasNonStandardStart());
    3377        9015 :         ie->SetCommentCount (commentCount);
    3378        9015 :         ie->SetVariationCount (varCount);
    3379        9015 :         ie->SetNagCount (nagCount);
    3380        9015 :         ie->SetFlag(IndexEntry::StrToFlagMask(ScidFlags), true);
    3381             : 
    3382        9015 :         std::copy_n(homePawnList, sizeof(homePawnList), ie->GetHomePawnData());
    3383             :         // Set other data updated by MakeHomePawnList():
    3384        9015 :         ie->SetPromotionsFlag (PromotionsFlag);
    3385        9015 :         ie->SetUnderPromoFlag (UnderPromosFlag);
    3386        9015 :         ie->SetNumHalfMoves (NumHalfMoves);
    3387        9015 :         ASSERT(AtEnd());
    3388        9015 :         ie->SetFinalMatSig(matsig_Make(CurrentPos->GetMaterial()));
    3389             : 
    3390             :         // Find the longest matching stored line for this game:
    3391        9015 :         uint storedLineCode = 0;
    3392        9015 :         if (!HasNonStandardStart()) {
    3393        9015 :             uint longestMatch = 0;
    3394     2298825 :             for (uint i = 1; i < StoredLine::count(); i++) {
    3395     2289810 :                 moveT* gameMove = FirstMove->next;
    3396     2289810 :                 uint matchLength = 0;
    3397     2289810 :                 FullMove m = StoredLine::getMove(i, matchLength);
    3398     2561650 :                 while (m) {
    3399     4837656 :                     if (gameMove->marker == END_MARKER
    3400     2418764 :                         ||  gameMove->moveData.from != m.getFrom()
    3401     2653135 :                         ||  gameMove->moveData.to != m.getTo())
    3402             :                     {
    3403     2282908 :                         matchLength = 0; break;
    3404             :                     }
    3405      135920 :                     gameMove = gameMove->next;
    3406      135920 :                     m = StoredLine::getMove(i, ++matchLength);
    3407             :                 }
    3408     2289810 :                 if (matchLength > longestMatch) {
    3409        6902 :                     longestMatch = matchLength;
    3410        6902 :                     storedLineCode = i;
    3411             :                 }
    3412             :             }
    3413             :         }
    3414        9015 :         ASSERT(storedLineCode == static_cast<byte>(storedLineCode));
    3415        9015 :         ie->SetStoredLineCode(static_cast<byte>(storedLineCode));
    3416             :     }
    3417             :     
    3418             :     // as each game entry length is coded on 17 bits, and game must fit in a block
    3419             :     // return an error if there is an overflow
    3420       10018 :     if (buf->GetByteCount() >= MAX_GAME_LENGTH) {
    3421           0 :       err = ERROR_GameFull;
    3422             :     }
    3423             : 
    3424       10018 :     return err;
    3425             : }
    3426             : 
    3427             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3428             : // Game::DecodeNextMove():
    3429             : //      Decodes one more mainline move of the game from the bytebuffer.
    3430             : //      Used in searches for speed, since it is usually possible to
    3431             : //      determine if a game matches the search criteria without decoding
    3432             : //      all of it.
    3433             : //      If the game flag KeepDecodedMoves is true, the move decodes is
    3434             : //      added normally. If it is false, only the current position is
    3435             : //      updated and the list of moves is not updated -- this is done
    3436             : //      in searches for speed.
    3437             : //      Returns OK if a move was found, or ERROR_EndOfMoveList if all the
    3438             : //      moves have been decoded. Returns ERROR_Game if some corruption was
    3439             : //      detected.
    3440             : //
    3441             : errorT
    3442           0 : Game::DecodeNextMove (ByteBuffer * buf, simpleMoveT * sm)
    3443             : {
    3444           0 :     ASSERT (buf != NULL);
    3445             :     errorT err;
    3446             :     byte b;
    3447             :     while (1) {
    3448           0 :         b = buf->GetByte ();
    3449           0 :         if (buf->Status() != OK) { return ERROR_Game; }
    3450           0 :         switch (b) {
    3451           0 :         case ENCODE_NAG:
    3452             :             // We ignore NAGS but have to read it from the buffer
    3453           0 :             buf->GetByte();
    3454           0 :             break;
    3455             : 
    3456           0 :         case ENCODE_COMMENT:  // We also ignore comments
    3457           0 :             break;
    3458             : 
    3459           0 :         case ENCODE_START_MARKER:
    3460             :             // Find the end of this variation and its children
    3461             :             uint nestCount;
    3462           0 :             nestCount= 1;
    3463           0 :             while (nestCount > 0) {
    3464           0 :                 b = buf->GetByte();
    3465           0 :                 if (buf->Status() != OK) { return ERROR_Game; }
    3466           0 :                 if (b == ENCODE_NAG) { buf->GetByte(); }
    3467           0 :                 else if (b == ENCODE_START_MARKER) { nestCount++; }
    3468           0 :                 else if (b == ENCODE_END_MARKER) { nestCount--; }
    3469           0 :                 else if (b == ENCODE_END_GAME) {
    3470             :                     // Open var at end of game: should never happen!
    3471           0 :                     return ERROR_Game;
    3472             :                 }
    3473             :             }
    3474           0 :             break;
    3475             : 
    3476           0 :         case ENCODE_END_MARKER:  // End marker in main game: error!
    3477           0 :             return ERROR_Game;
    3478             : 
    3479           0 :         case ENCODE_END_GAME:  // We reached the end of the game:
    3480           0 :             return ERROR_EndOfMoveList;
    3481             : 
    3482           0 :         default:  // It's a move in the game; decode it:
    3483             :             simpleMoveT tempMove;
    3484           0 :             if (!sm) { sm = &tempMove; }
    3485           0 :             err = decodeMove (buf, sm, b, currentPos());
    3486           0 :             if (err != OK)  { return err; }
    3487           0 :             if (KeepDecodedMoves) {
    3488           0 :                 AddMove(sm);
    3489             :             } else {
    3490           0 :                 CurrentPos->DoSimpleMove (sm);
    3491             :             }
    3492           0 :             return OK;
    3493             :         }
    3494           0 :     }
    3495             : 
    3496             :     // We never reach here:
    3497             :     ASSERT(0);
    3498             :     return ERROR_Game;
    3499             : }
    3500             : 
    3501             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3502             : // Game::DecodeStart():
    3503             : //      Decodes the starting information from the game's on-disk
    3504             : //      representation in the bytebuffer. After this is called,
    3505             : //      DecodeNextMove() can be called to decode each successive
    3506             : //      mainline move.
    3507             : //
    3508        1018 : errorT Game::DecodeStart(ByteBuffer* buf, bool decodeTags) {
    3509        1018 :     ASSERT(buf != NULL);
    3510        1018 :     errorT err = buf->Status();
    3511        1018 :     if (err != OK)
    3512           0 :         return err;
    3513             : 
    3514        1018 :     err = decodeTags ? DecodeTags(buf, true) : skipTags(buf);
    3515        1018 :     if (err != OK)
    3516           0 :         return err;
    3517             : 
    3518             :     // Now the flags:
    3519        1018 :     byte flags = buf->GetByte();
    3520        1018 :     if (flags & 2) { PromotionsFlag = true; }
    3521             : 
    3522             :     // Now decode the startBoard, if there is one.
    3523        1018 :     if (flags & 1) {
    3524             :         char * tempStr;
    3525           0 :         buf->GetTerminatedString (&tempStr);
    3526           0 :         if ((err = buf->Status()) != OK)
    3527           0 :             return err;
    3528             : 
    3529           0 :         StartPos = std::make_unique<Position>();
    3530           0 :         err = StartPos->ReadFromFEN (tempStr);
    3531           0 :         if (err != OK) {
    3532           0 :             StartPos = nullptr;
    3533           0 :             return err;
    3534             :         }
    3535           0 :         *CurrentPos = *StartPos;
    3536             :     }
    3537             : 
    3538        1018 :     return err;
    3539             : }
    3540             : 
    3541             : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3542             : // Game::Decode():
    3543             : //      Decodes a game from its on-disk representation in a bytebuffer.
    3544             : //      Decodes all the information: comments, variations, non-standard
    3545             : //      tags, etc, or selectively can ignore comments and/or tags for
    3546             : //      speed if the argument "flags" indicates.
    3547             : //
    3548             : errorT
    3549        1018 : Game::Decode (ByteBuffer * buf, byte flags)
    3550             : {
    3551        1018 :     ASSERT (buf != NULL);
    3552             : 
    3553        1018 :     Clear();
    3554        1018 :     errorT err = DecodeStart(buf, flags & GAME_DECODE_TAGS);
    3555        1018 :     if (err == OK)
    3556        1018 :         err = DecodeVariation (buf, flags, 0);
    3557             : 
    3558        1018 :     if (err == OK && (flags & GAME_DECODE_COMMENTS))
    3559        1018 :         err = decodeComments (buf, FirstMove);
    3560             : 
    3561        1018 :     return (err != OK) ? err : buf->Status();
    3562             : }
    3563             : 
    3564             : //////////////////////////////////////////////////////////////////////
    3565             : //  EOF:    game.cpp
    3566             : //////////////////////////////////////////////////////////////////////

Generated by: LCOV version 1.13