1 //////////////////////////////////////////////////////////////////////
2 //
3 //  FILE:       game.h
4 //              Game class for Scid.
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 #ifndef SCID_GAME_H
17 #define SCID_GAME_H
18 
19 #include "common.h"
20 #include "date.h"
21 #include "indexentry.h"
22 #include "matsig.h"
23 #include "movetree.h"
24 #include "namebase.h"
25 #include "position.h"
26 #include <forward_list>
27 #include <memory>
28 #include <string>
29 #include <vector>
30 class ByteBuffer;
31 class TextBuffer;
32 
33 void transPieces(char *s);
34 char transPiecesChar(char c);
35 
36 // Piece letters translation
37 extern int language; // default to english
38 //  0 = en, 1 = fr, 2 = es, 3 = de
39 extern const char * langPieces[];
40 
41 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42 //  Game: Constants
43 
44 // Common NAG Annotation symbol values:
45 const byte
46     NAG_GoodMove = 1,
47     NAG_PoorMove = 2,
48     NAG_ExcellentMove = 3,
49     NAG_Blunder = 4,
50     NAG_InterestingMove = 5,
51     NAG_DubiousMove = 6,
52     NAG_OnlyMove = 8, // new
53     NAG_Equal = 10,
54     NAG_Unclear = 13,
55     NAG_WhiteSlight = 14,
56     NAG_BlackSlight = 15,
57     NAG_WhiteClear = 16,
58     NAG_BlackClear = 17,
59     NAG_WhiteDecisive = 18,
60     NAG_BlackDecisive = 19,
61     NAG_WhiteCrushing = 20,
62     NAG_BlackCrushing = 21,
63     NAG_ZugZwang = 22, // new
64     NAG_BlackZugZwang = 23, // new
65     NAG_MoreRoom = 26, // new
66     NAG_DevelopmentAdvantage = 35,  // new
67     NAG_WithInitiative = 36, //new
68     NAG_WithAttack = 40, // new
69     NAG_WithBlackAttack = 41, // new
70     NAG_Compensation = 44,      // from Whites perspective
71     NAG_SlightCentre = 48,      // from Whites perspective
72     NAG_Centre = 50,            // new
73     NAG_SlightKingSide = 54,    // from Whites perspective
74     NAG_ModerateKingSide = 56,  // from Whites perspective
75     NAG_KingSide = 58,          // from Whites perspective
76     NAG_SlightQueenSide = 60,   // from Whites perspective
77     NAG_ModerateQueenSide = 62, // from Whites perspective
78     NAG_QueenSide = 64,         // from Whites perspective
79     NAG_SlightCounterPlay = 130, // new
80     NAG_CounterPlay = 132, // new
81     NAG_DecisiveCounterPlay = 134, // new
82     NAG_BlackSlightCounterPlay = 131, // new
83     NAG_BlackCounterPlay = 133, // new
84     NAG_BlackDecisiveCounterPlay = 135, // new
85     NAG_TimeLimit = 136, // new
86     NAG_WithIdea = 140, // new
87     NAG_BetterIs = 142, // new
88     NAG_VariousMoves = 144, // new
89     NAG_Comment = 145, // new
90     NAG_Novelty = 146,
91     NAG_WeakPoint = 147, // new
92     NAG_Ending = 148, // new
93     NAG_File = 149, // new
94     NAG_Diagonal = 150, // new
95     NAG_BishopPair = 151, // new
96     NAG_OppositeBishops = 153, // new
97     NAG_SameBishops = 154, // new
98     NAG_Etc = 190, // new
99     NAG_DoublePawns = 191, // new
100     NAG_SeparatedPawns = 192, // new
101     NAG_UnitedPawns = 193, // new
102     NAG_Diagram = 201,  // Scid-specific NAGs start at 201.
103     NAG_See = 210,  // new
104     NAG_Mate = 211, // new
105     NAG_PassedPawn = 212, // new
106     NAG_MorePawns = 213, //new
107     NAG_With = 214, // new
108     NAG_Without = 215;
109 
110 // MAX_NAGS: Maximum id of NAG codes
111 const byte MAX_NAGS_ARRAY = 215;
112 
113 // patternT structure: a pattern filter for material searches.
114 //    It can specify, for example, a white Pawn on the f-fyle, or
115 //    a black Bishop on f2 and white King on e1.
116 struct patternT
117 {
118     pieceT     pieceMatch;  // EMPTY, WK, BK, etc...
119     rankT      rankMatch;   // RANK_1 .. RANK_8 or NO_RANK
120     fyleT      fyleMatch;   // A_FYLE .. H_FYLE or NO_FYLE
121     byte       flag;        // 0 means this pattern must NOT occur.
122     patternT * next;
123 };
124 
125 #define GAME_DECODE_NONE 0
126 #define GAME_DECODE_TAGS 1
127 #define GAME_DECODE_COMMENTS 2
128 #define GAME_DECODE_ALL 3
129 
130 enum gameExactMatchT {
131     GAME_EXACT_MATCH_Exact = 0,
132     GAME_EXACT_MATCH_Pawns,
133     GAME_EXACT_MATCH_Fyles,
134     GAME_EXACT_MATCH_Material
135 };
136 
137 enum gameFormatT {
138     PGN_FORMAT_Plain = 0,   // Plain regular PGN output
139     PGN_FORMAT_HTML = 1,    // HTML format
140     PGN_FORMAT_LaTeX = 2,   // LaTeX (with chess12 package) format
141     PGN_FORMAT_Color = 3    // PGN, with color tags <red> etc
142 };
143 
144 #define PGN_STYLE_TAGS             1
145 #define PGN_STYLE_COMMENTS         2
146 #define PGN_STYLE_VARS             4
147 #define PGN_STYLE_INDENT_COMMENTS  8
148 #define PGN_STYLE_INDENT_VARS     16
149 #define PGN_STYLE_SYMBOLS         32   // e.g. "! +-" instead of "$2 $14"
150 #define PGN_STYLE_SHORT_HEADER    64
151 #define PGN_STYLE_MOVENUM_SPACE  128   // Space after move numbers.
152 #define PGN_STYLE_COLUMN         256   // Column style: one move per line.
153 #define PGN_STYLE_SCIDFLAGS      512
154 #define PGN_STYLE_STRIP_MARKS   1024   // Strip [%mark] and [%arrow] codes.
155 #define PGN_STYLE_NO_NULL_MOVES 2048   // Convert null moves to comments.
156 #define PGN_STYLE_UNICODE       4096   // Use U+2654..U+2659 for figurine
157 
158 
159 void  game_printNag (byte nag, char * str, bool asSymbol, gameFormatT format);
160 byte game_parseNag(std::pair<const char*, const char*> strview);
161 
162 uint strGetRatingType (const char * name);
163 
164 //////////////////////////////////////////////////////////////////////
165 //  Game:  Class Definition
166 
167 class Game {
168     // Header data: tag pairs
169     std::vector<std::pair<std::string, std::string> > extraTags_;
170     std::string WhiteStr;
171     std::string BlackStr;
172     std::string EventStr;
173     std::string SiteStr;
174     std::string RoundStr;
175     dateT       Date;
176     dateT       EventDate;
177     ecoT        EcoCode;
178     eloT        WhiteElo;
179     eloT        BlackElo;
180     byte        WhiteRatingType;
181     byte        BlackRatingType;
182     resultT     Result;
183     char        ScidFlags[22];
184 
185     // Position and moves
186     byte        moveChunkUsed_;
187     std::forward_list<std::unique_ptr<moveT[]> > moveChunks_;
188     std::unique_ptr<Position> StartPos;
189     std::unique_ptr<Position> CurrentPos{new Position};
190     moveT*      FirstMove;
191     moveT*      CurrentMove;
192     uint        VarDepth;     // Current variation depth.
193     ushort      NumHalfMoves; // Total half moves in the main line.
194 
195     // TODO: The following variables should not be part of this class.
196     bool        PromotionsFlag;   // Used by MaterialMatch
197     bool        KeepDecodedMoves; // Used by MaterialMatch end ExactMatch
198 
199     eloT        WhiteEstimateElo;
200     eloT        BlackEstimateElo;
201 
202     uint        NumMovesPrinted; // Used in recursive WriteMoveList method.
203     uint        PgnStyle;        // see PGN_STYLE macros above.
204     gameFormatT PgnFormat;       // see PGN_FORMAT macros above.
205     uint        HtmlStyle;       // HTML diagram style, see DumpHtmlBoard method in position.cpp.
206 
207 private:
208     Game(const Game&);
209     moveT* allocMove();
210     moveT* NewMove(markerT marker);
211     void ClearMoves();
212     bool MakeHomePawnList(byte* pbPawnList);
213     errorT DecodeVariation(ByteBuffer* buf, byte flags, uint level);
214     errorT WritePGN(TextBuffer* tb);
215 
216     /**
217      * Contains the information of the current position in the game, so that
218      * after an operation that alters the location, it can be restored.
219      */
220     struct GameSavedPos {
221         Position pos;
222         moveT* move;
223         uint varDepth;
224     };
225 
226 public:
Game()227     Game() { Clear(); }
228     void Clear();
229     void strip(bool variations, bool comments, bool NAGs);
230 
231     bool HasNonStandardStart(char* outFEN = nullptr) const {
232         if (!StartPos)
233             return false;
234         if (outFEN)
235             StartPos->PrintFEN(outFEN, FEN_ALL_FIELDS);
236         return true;
237     }
238 
239     /// Setup the start position from a FEN string and remove all the moves.
240     /// If the FEN is invalid the game is not changed.
241     errorT SetStartFen(const char* fenStr);
242 
SetScidFlags(const char * s,size_t len)243     void SetScidFlags(const char* s, size_t len) {
244         constexpr size_t size = sizeof(ScidFlags) / sizeof(*ScidFlags);
245         std::fill_n(ScidFlags, size, 0);
246         std::copy_n(s, std::min(size - 1, len), ScidFlags);
247     }
SetScidFlags(const char * s)248     void SetScidFlags(const char* s) { SetScidFlags(s, std::strlen(s)); }
249 
GetNumHalfMoves()250     ushort GetNumHalfMoves() { return NumHalfMoves; }
251 
252     //////////////////////////////////////////////////////////////
253     // Functions to add or delete moves:
254     //
255     errorT AddMove(const simpleMoveT* sm);
256     errorT AddVariation();
257     errorT DeleteVariation();
258     errorT FirstVariation();
259     errorT MainVariation();
260     void Truncate();
261     void TruncateStart();
262 
263     //////////////////////////////////////////////////////////////
264     // Functions that move the current location (only CurrentPos,
265     // CurrentMove and VarDepth are modified by these functions):
266     //
267     errorT MoveForward();
268     errorT MoveBackup();
269     errorT MoveIntoVariation(uint varNumber);
270     errorT MoveExitVariation();
271     errorT MoveForwardInPGN();
272     errorT MoveToLocationInPGN(unsigned stopLocation);
273     void MoveToStart();
MoveToPly(int hmNumber)274     void MoveToPly(int hmNumber) { // Move to a specified
275         MoveToStart();             // mainline ply in the game.
276         for (int i = 0; i < hmNumber; ++i)
277             MoveForward();
278     }
currentLocation()279     GameSavedPos currentLocation() const {
280         return GameSavedPos{*CurrentPos, CurrentMove, VarDepth};
281     }
restoreLocation(const GameSavedPos & savedPos)282     void restoreLocation(const GameSavedPos& savedPos) {
283         *CurrentPos = savedPos.pos;
284         CurrentMove = savedPos.move;
285         VarDepth = savedPos.varDepth;
286     }
287 
288     //////////////////////////////////////////////////////////////
289     // Functions that get information about the current location.
290     //
currentPos()291     const Position* currentPos() const { return CurrentPos.get(); }
GetCurrentPos()292     Position* GetCurrentPos() { // Deprecated, use the const version
293         return CurrentPos.get();
294     }
GetCurrentMove()295     simpleMoveT* GetCurrentMove() { // Deprecated
296         return CurrentMove->endMarker() ? nullptr : &CurrentMove->moveData;
297     }
GetCurrentPly()298     ushort GetCurrentPly() const {
299         auto ply = CurrentPos->GetPlyCounter();
300         return StartPos ? ply - StartPos->GetPlyCounter() : ply;
301     }
GetNumVariations()302     uint GetNumVariations() const { return CurrentMove->numVariations; }
303 
304     // Each variation has a "level" and a "number".
305     // - "level" is the number of times that is necessary to call
306     //   MoveExitVariation() to reach the main line.
307     // - "number" is the ordered position in the list of variations for the
308     // current root position (first variation is number 0).
309     // The main line is 0,0.
GetVarLevel()310     uint GetVarLevel() const { return VarDepth; }
GetVarNumber()311     uint GetVarNumber() const {
312         if (VarDepth != 0) {
313             uint varNumber = 0;
314             auto moves = CurrentMove->getParent();
315             for (auto parent = moves.first; parent; varNumber++) {
316                 parent = parent->varChild;
317                 if (parent == moves.second)
318                     return varNumber;
319             }
320         }
321         return 0; // returns 0 if in main line
322     }
323 
324     unsigned GetLocationInPGN() const;
325     unsigned GetPgnOffset() const;
326 
AtVarStart()327     bool AtVarStart() const { return CurrentMove->prev->startMarker(); }
AtVarEnd()328     bool AtVarEnd() const { return CurrentMove->endMarker(); }
AtStart()329     bool AtStart() const { return (VarDepth == 0 && AtVarStart()); }
AtEnd()330     bool AtEnd() const { return (VarDepth == 0 && AtVarEnd()); }
331 
332     //////////////////////////////////////////////////////////////
333     // Functions that get/set information about the last/next move.
334     // Notice: when location is at the start of the game or a variation,
335     // infomation are stored into the START_MARKER.
336     //
337     errorT AddNag(byte nag);
338     errorT RemoveNag(bool isMoveNag);
ClearNags()339     void ClearNags() {
340         CurrentMove->prev->nagCount = 0;
341         CurrentMove->prev->nags[0] = 0;
342     }
GetNags()343     byte* GetNags() const { return CurrentMove->prev->nags; }
GetNextNags()344     byte* GetNextNags() const { return CurrentMove->nags; }
345 
346     /**
347      * Return the comment on the move previously played by CurrentPos->ToMove
348      * If there are no previous moves, return an empty comment.
349      */
GetPreviousMoveComment()350     const char* GetPreviousMoveComment() const {
351         const moveT* move = CurrentMove->getPrevMove();
352         if (move)
353             move = move->getPrevMove();
354 
355         return (move) ? move->comment.c_str() : "";
356     }
GetMoveComment()357     const char* GetMoveComment() const {
358         return CurrentMove->prev->comment.c_str();
359     }
accessMoveComment()360     std::string& accessMoveComment() { return CurrentMove->prev->comment; }
361     void SetMoveComment(const char* comment);
362 
363     const char* GetNextSAN();
364     void GetSAN(char* str);
365     void GetPrevSAN(char* str);
366     void GetPrevMoveUCI(char* str) const;
367     void GetNextMoveUCI(char* str);
368 
369     //////////////////////////////////////////////////////////////
370     // Functions that get/set the tag pairs:
371     //
372     void AddPgnTag(const char* tag, const char* value);
373     bool RemoveExtraTag(const char* tag);
374     const char* FindExtraTag(const char* tag) const;
375     std::string& accessTagValue(const char* tag, size_t tagLen);
decltype(extraTags_)376     const decltype(extraTags_) & GetExtraTags() const { return extraTags_; }
ClearExtraTags()377     void ClearExtraTags() { extraTags_.clear(); }
378 
379     errorT LoadStandardTags(const IndexEntry* ie, const NameBase* nb);
380 
SetEventStr(const char * str)381     void     SetEventStr (const char * str) { EventStr = str; }
SetSiteStr(const char * str)382     void     SetSiteStr  (const char * str) { SiteStr  = str; }
SetWhiteStr(const char * str)383     void     SetWhiteStr (const char * str) { WhiteStr = str; }
SetBlackStr(const char * str)384     void     SetBlackStr (const char * str) { BlackStr = str; }
SetRoundStr(const char * str)385     void     SetRoundStr (const char * str) { RoundStr = str; }
SetDate(dateT date)386     void     SetDate (dateT date)    { Date = date; }
SetEventDate(dateT date)387     void     SetEventDate (dateT date)  { EventDate = date; }
SetResult(resultT res)388     void     SetResult (resultT res) { Result = res; }
SetWhiteElo(eloT elo)389     void     SetWhiteElo (eloT elo)  { WhiteElo = elo; }
SetBlackElo(eloT elo)390     void     SetBlackElo (eloT elo)  { BlackElo = elo; }
SetWhiteRatingType(byte b)391     void     SetWhiteRatingType (byte b) { WhiteRatingType = b; }
SetBlackRatingType(byte b)392     void     SetBlackRatingType (byte b) { BlackRatingType = b; }
393     int setRating(colorT col, const char* ratingType, size_t ratingTypeLen,
394                   std::pair<const char*, const char*> rating);
SetEco(ecoT eco)395     void     SetEco (ecoT eco)       { EcoCode = eco; }
GetEventStr()396     const char* GetEventStr () const { return EventStr.c_str(); }
GetSiteStr()397     const char* GetSiteStr ()  const { return SiteStr.c_str();  }
GetWhiteStr()398     const char* GetWhiteStr () const { return WhiteStr.c_str(); }
GetBlackStr()399     const char* GetBlackStr () const { return BlackStr.c_str(); }
GetRoundStr()400     const char* GetRoundStr () const { return RoundStr.c_str(); }
GetDate()401     dateT    GetDate ()        const { return Date; }
GetEventDate()402     dateT    GetEventDate ()   const { return EventDate; }
GetResult()403     resultT  GetResult ()      const { return Result; }
GetWhiteElo()404     eloT     GetWhiteElo ()    const { return WhiteElo; }
GetBlackElo()405     eloT     GetBlackElo ()    const { return BlackElo; }
GetWhiteEstimateElo()406     eloT     GetWhiteEstimateElo() const { return WhiteEstimateElo; }
GetBlackEstimateElo()407     eloT     GetBlackEstimateElo() const { return BlackEstimateElo; }
GetWhiteRatingType()408     byte     GetWhiteRatingType () const { return WhiteRatingType; }
GetBlackRatingType()409     byte     GetBlackRatingType () const { return BlackRatingType; }
GetEco()410     ecoT     GetEco ()         const { return EcoCode; }
411     eloT     GetAverageElo ();
412 
413     // PGN conversion
414     bool      CommentEmpty ( const char * comment);
415     void      WriteComment (TextBuffer * tb, const char * preStr,
416                             const char * comment, const char * postStr);
417     errorT    WriteMoveList(TextBuffer* tb, moveT* oldCurrentMove,
418                             bool printMoveNum, bool inComment);
419     std::pair<const char*, unsigned> WriteToPGN (uint lineWidth = 0,
420                                                  bool NewLineAtEnd = false,
421                                                  bool newLineToSpaces = true);
422 
ResetPgnStyle(void)423     void      ResetPgnStyle (void) { PgnStyle = 0; }
ResetPgnStyle(uint flag)424     void      ResetPgnStyle (uint flag) { PgnStyle = flag; }
425 
GetPgnStyle()426     uint      GetPgnStyle () { return PgnStyle; }
SetPgnStyle(uint mask,bool setting)427     void      SetPgnStyle (uint mask, bool setting) {
428         if (setting) { AddPgnStyle (mask); } else { RemovePgnStyle (mask); }
429     }
AddPgnStyle(uint mask)430     void      AddPgnStyle (uint mask) { PgnStyle |= mask; }
RemovePgnStyle(uint mask)431     void      RemovePgnStyle (uint mask) { PgnStyle &= ~mask; }
432 
SetPgnFormat(gameFormatT gf)433     void      SetPgnFormat (gameFormatT gf) { PgnFormat = gf; }
434     bool      SetPgnFormatFromString (const char * str);
435     static bool PgnFormatFromString (const char * str, gameFormatT * fmt);
IsPlainFormat()436     bool      IsPlainFormat () { return (PgnFormat == PGN_FORMAT_Plain); }
IsHtmlFormat()437     bool      IsHtmlFormat  () { return (PgnFormat == PGN_FORMAT_HTML); }
IsLatexFormat()438     bool      IsLatexFormat () { return (PgnFormat == PGN_FORMAT_LaTeX); }
IsColorFormat()439     bool      IsColorFormat () { return (PgnFormat == PGN_FORMAT_Color); }
440 
SetHtmlStyle(uint style)441     void      SetHtmlStyle (uint style) { HtmlStyle = style; }
GetHtmlStyle()442     uint      GetHtmlStyle () { return HtmlStyle; }
443 
444     errorT    GetPartialMoveList (DString * str, uint plyCount);
445 
446     bool      MaterialMatch (ByteBuffer * buf, byte * min, byte * max,
447                              patternT * pattern, int minPly, int maxPly,
448                              int matchLength,
449                              bool oppBishops, bool sameBishops,
450                              int minDiff, int maxDiff);
451     bool      ExactMatch (Position * pos, ByteBuffer * buf, simpleMoveT * sm,
452                           gameExactMatchT searchType, bool * neverMatch);
453     bool      VarExactMatch (Position * searchPos, gameExactMatchT searchType);
ExactMatch(Position * pos,ByteBuffer * buf,simpleMoveT * sm)454     inline bool ExactMatch (Position * pos, ByteBuffer * buf, simpleMoveT * sm)
455       { return ExactMatch (pos, buf, sm, GAME_EXACT_MATCH_Exact, NULL); }
ExactMatch(Position * pos,ByteBuffer * buf,simpleMoveT * sm,bool * neverMatch)456     inline bool ExactMatch (Position * pos, ByteBuffer * buf, simpleMoveT * sm,
457                             bool * neverMatch)
458       { return ExactMatch (pos, buf, sm, GAME_EXACT_MATCH_Exact, neverMatch); }
ExactMatch(Position * pos,ByteBuffer * buf,simpleMoveT * sm,gameExactMatchT searchType)459     inline bool ExactMatch (Position * pos, ByteBuffer * buf, simpleMoveT * sm,
460                             gameExactMatchT searchType)
461       { return ExactMatch (pos, buf, sm, searchType, NULL); }
462 
463     errorT    Encode (ByteBuffer * buf, IndexEntry * ie);
464     errorT    DecodeStart(ByteBuffer* buf, bool decodeTags = false);
465     errorT    DecodeNextMove (ByteBuffer * buf, simpleMoveT * sm);
466     errorT    Decode (ByteBuffer * buf, byte flags);
467     errorT    DecodeTags (ByteBuffer * buf, bool storeTags);
468 
469     Game* clone();
470 };
471 
472 namespace gamevisit {
473 
tags_STR(const Game & game,TFunc visitor)474 template <typename TFunc> void tags_STR(const Game& game, TFunc visitor) {
475 	char dateBuf[16];
476 	visitor("Event", game.GetEventStr());
477 	visitor("Site", game.GetSiteStr());
478 	date_DecodeToString(game.GetDate(), dateBuf);
479 	visitor("Date", dateBuf);
480 	visitor("Round", game.GetRoundStr());
481 	visitor("White", game.GetWhiteStr());
482 	visitor("Black", game.GetBlackStr());
483 	visitor("Result", RESULT_LONGSTR[game.GetResult()]);
484 }
485 
tags_extra(const Game & game,TFunc visitor)486 template <typename TFunc> void tags_extra(const Game& game, TFunc visitor) {
487 	char strBuf[256];
488 	if (auto elo = game.GetWhiteElo()) {
489 		std::string rType = "White";
490 		rType.append(ratingTypeNames[game.GetWhiteRatingType()]);
491 		visitor(rType.c_str(), std::to_string(elo).c_str());
492 	}
493 	if (auto elo = game.GetBlackElo()) {
494 		std::string rType = "Black";
495 		rType.append(ratingTypeNames[game.GetBlackRatingType()]);
496 		visitor(rType.c_str(), std::to_string(elo).c_str());
497 	}
498 	if (game.GetEco() != ECO_None) {
499 		eco_ToExtendedString(game.GetEco(), strBuf);
500 		visitor("ECO", strBuf);
501 	}
502 	if (game.GetEventDate() != ZERO_DATE) {
503 		date_DecodeToString(game.GetEventDate(), strBuf);
504 		visitor("EventDate", strBuf);
505 	}
506 	// TODO:
507 	// if (*ScidFlags)
508 	//	visitor("ScidFlags", ScidFlags);
509 
510 	for (auto& e : game.GetExtraTags()) {
511 		visitor(e.first.c_str(), e.second.c_str());
512 	}
513 	if (game.HasNonStandardStart(strBuf)) {
514 		visitor("FEN", strBuf);
515 	}
516 }
517 
518 } // namespace gamevisit
519 
520 namespace gamepos {
521 
522 struct GamePos {
523 	uint32_t RAVdepth;
524 	uint32_t RAVnum;
525 	std::string FEN; // "Forsyth-Edwards Notation" describing the position.
526 	std::vector<int> NAGs;   // "Numeric Annotation Glyph"
527 	std::string comment;     // text annotation of the position.
528 	std::string lastMoveSAN; // move that was played to reach the position.
529 };
530 
531 /**
532  * Iterate all the positions of a game and store the corresponding GamePos
533  * objects into a container.
534  *
535  * The order of positions and of Recursive Annotation Variations (RAV) follows
536  * the PGN standard: "The alternate move sequence given by an RAV is one that
537  * may be legally played by first unplaying the move that appears immediately
538  * prior to the RAV. Because the RAV is a recursive construct, it may be nested"
539  * Each position have a RAVdepth and a RAVnum that allows to follow a
540  * variation from any given position:
541  * - skip all the next positions with a bigger RAVdepth
542  * - the variation ends with:
543  *   - a lower RAVdepth or
544  *   - an equal RAVdepth but different RAVnum or
545  *   - the end of @e dest
546  * @param game: reference to the Game object where the positions are read.
547  * @param dest: the container where the GamePos objects are appended.
548  */
549 template <typename TCont>
collectPositions(Game & game,TCont & dest)550 inline void collectPositions(Game& game, TCont& dest) {
551 	do {
552 		if (game.AtVarStart() && !game.AtStart())
553 			continue;
554 
555 		dest.emplace_back();
556 		auto& gamepos = dest.back();
557 		gamepos.RAVdepth = game.GetVarLevel();
558 		gamepos.RAVnum = game.GetVarNumber();
559 		char strBuf[256];
560 		game.currentPos()->PrintFEN(strBuf, FEN_ALL_FIELDS);
561 		gamepos.FEN = strBuf;
562 		for (byte* nag = game.GetNags(); *nag; nag++) {
563 			gamepos.NAGs.push_back(*nag);
564 		}
565 		gamepos.comment = game.GetMoveComment();
566 		game.GetPrevSAN(strBuf);
567 		gamepos.lastMoveSAN = strBuf;
568 
569 	} while (game.MoveForwardInPGN() == OK);
570 }
571 
572 /**
573  * Returns all the positions of a game
574  * @param game: reference to the Game object where the positions are read.
575  * @returns a std::vector containing the GamePos objects corresponding to all
576  * the positions of @e game.
577  */
collectPositions(Game & game)578 inline std::vector<GamePos> collectPositions(Game& game) {
579 	std::vector<GamePos> res;
580 	game.MoveToStart();
581 	collectPositions(game, res);
582 	return res;
583 }
584 
585 } // namespace gamepos
586 
587 #endif  // #ifndef SCID_GAME_H
588 
589 //////////////////////////////////////////////////////////////////////
590 //  EOF:    game.h
591 //////////////////////////////////////////////////////////////////////
592