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