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