LCOV - code coverage report
Current view: top level - src - game.cpp (source / functions) Hit Total Coverage
Test: test_coverage.info Lines: 738 1905 38.7 %
Date: 2017-06-21 14:32:49 Functions: 49 88 55.7 %

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

Generated by: LCOV version 1.12