Subversion Repositories Games.Chess Giants

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
112 pmbaty 1
/*
2
    Protector -- a UCI chess engine
3
 
4
    Copyright (C) 2009-2010 Raimund Heid (Raimund_Heid@yahoo.com)
5
 
6
    This program is free software: you can redistribute it and/or modify
7
    it under the terms of the GNU General Public License as published by
8
    the Free Software Foundation, either version 3 of the License, or
9
    (at your option) any later version.
10
 
11
    This program is distributed in the hope that it will be useful,
12
    but WITHOUT ANY WARRANTY; without even the implied warranty of
13
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
    GNU General Public License for more details.
15
 
16
    You should have received a copy of the GNU General Public License
17
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 
19
*/
20
 
21
#include <assert.h>
22
#include <stdlib.h>
23
#include <string.h>
24
#include <ctype.h>
25
#include "io.h"
26
#include "pgn.h"
27
#include "bitboard.h"
28
#include "position.h"
29
#include "movegeneration.h"
30
#include "fen.h"
31
 
32
#define BUFSIZE 8192
33
#define INCREMENT 1024
34
 
35
#define STATE_INIT      0
36
#define STATE_1         1
37
#define STATE_2         2
38
#define STATE_IGNORE    3
39
 
40
static char pieceName[16];
41
 
42
/*
43
 ******************************************************************************
44
 *
45
 *   File operations
46
 *
47
 ******************************************************************************
48
 */
49
 
50
static int scanForIndex(PGNFile * pgnfile, int state, char buffer[],
51
                        size_t bufsize, size_t offset)
52
{
53
   size_t i = 0;
54
 
55
   while (i < bufsize)
56
   {
57
      switch (buffer[i++])
58
      {
59
      case '\n':
60
 
61
         if (state < STATE_IGNORE)
62
         {
63
            state++;
64
         }
65
 
66
         break;
67
 
68
      case '\r':
69
      case ' ':
70
         continue;
71
 
72
      case '{':
73
 
74
         if (state < STATE_IGNORE)
75
         {
76
            state = STATE_IGNORE;
77
         }
78
         else
79
         {
80
            state++;
81
         }
82
 
83
         break;
84
 
85
      case '}':
86
 
87
         if (state > STATE_IGNORE)
88
         {
89
            state--;
90
         }
91
         else
92
         {
93
            state = 0;
94
         }
95
 
96
         break;
97
 
98
      case '[':
99
 
100
         if (state == 2)
101
         {
102
            if (pgnfile->numGames + 1 == pgnfile->indexSize)
103
            {
104
               pgnfile->indexSize += INCREMENT;
105
               pgnfile->index = (long *) realloc(pgnfile->index,
106
                                                 pgnfile->indexSize *
107
                                                 sizeof(long));
108
            }
109
 
110
            pgnfile->index[pgnfile->numGames++] = (long) (i + offset - 1L);
111
            assert(pgnfile->numGames < pgnfile->indexSize);
112
         }
113
 
114
         break;
115
 
116
      default:
117
 
118
         if (state != STATE_IGNORE)
119
         {
120
            state = 0;
121
         }
122
      }
123
   }
124
 
125
   return state;
126
}
127
 
128
static void buildIndex(PGNFile * pgnfile)
129
{
130
   char buffer[BUFSIZE];
131
   int state = STATE_2;
132
   size_t numRead;
133
 
134
   rewind(pgnfile->file);
135
 
136
   do
137
   {
138
      long pos = ftell(pgnfile->file);
139
 
140
      numRead = fread(buffer, 1, BUFSIZE, pgnfile->file);
141
      state = scanForIndex(pgnfile, state, buffer, numRead, pos);
142
   }
143
   while (numRead == BUFSIZE);
144
 
145
   fseek(pgnfile->file, 0, SEEK_END);
146
   pgnfile->index[pgnfile->numGames] = ftell(pgnfile->file) + 1;
147
}
148
 
149
int openPGNFile(PGNFile * pgnfile, const char *filename)
150
{
151
   pgnfile->index = (long *) malloc(INCREMENT * sizeof(long));
152
 
153
   pgnfile->indexSize = INCREMENT;
154
   pgnfile->numGames = 0;
155
   pgnfile->file = fopen(filename, "rb");
156
 
157
   if (pgnfile->index != 0 && pgnfile->file != 0)
158
   {
159
      buildIndex(pgnfile);
160
      return 0;
161
   }
162
   else
163
   {
164
      return -1;
165
   }
166
}
167
 
168
void closePGNFile(PGNFile * pgnfile)
169
{
170
   if (pgnfile->index != 0)
171
   {
172
      free(pgnfile->index);
173
   }
174
 
175
   pgnfile->indexSize = 0;
176
   pgnfile->numGames = 0;
177
 
178
   if (pgnfile->file != 0)
179
   {
180
      fclose(pgnfile->file);
181
      pgnfile->file = 0;
182
   }
183
}
184
 
185
static char *getGameText(PGNFile * pgnfile, int number)
186
{
187
   long start, end, length;
188
   char *buffer;
189
 
190
   if (number < 1 || number > pgnfile->numGames)
191
   {
192
      return 0;
193
   }
194
 
195
   start = pgnfile->index[number - 1];
196
   end = pgnfile->index[number] - 1;
197
   length = (int) (end - start);
198
 
199
   if ((buffer = malloc(length + 1)) == NULL)
200
   {
201
      return 0;
202
   }
203
 
204
   fseek(pgnfile->file, start, SEEK_SET);
205
   fread(buffer, 1, length, pgnfile->file);
206
   buffer[length] = '\0';
207
   trim(buffer);
208
 
209
   return buffer;
210
}
211
 
212
/*
213
 ******************************************************************************
214
 *
215
 *   Object generation and destruction
216
 *
217
 ******************************************************************************
218
 */
219
 
220
static Gamemove *getGamemove(const Position * position,
221
                             Gamemove * previousMove, PGNGame * game)
222
{
223
   Gamemove *new = &(game->moveHeap[game->nextMoveFromHeap++]);
224
 
225
   new->from = NO_SQUARE;
226
   new->to = NO_SQUARE;
227
   new->newPiece = NO_PIECE;
228
   new->position = *position;
229
   new->previousMove = previousMove;
230
   new->nextMove = new->alternativeMove = 0;
231
   new->comment = new->glyphs = 0;
232
 
233
   return new;
234
}
235
 
236
Move gameMove2Move(const Gamemove * gamemove)
237
{
238
   return getPackedMove(gamemove->from, gamemove->to, gamemove->newPiece);
239
}
240
 
241
void initializePGNGame(PGNGame * game)
242
{
243
   game->setup[0] = game->fen[0] = '\0';
244
   game->moveText = 0;
245
   game->firstMove = game->lastMove = 0;
246
   game->nextMoveFromHeap = 0;
247
}
248
 
249
void resetPGNGame(PGNGame * game)
250
{
251
   if (game->moveText != 0)
252
   {
253
      free(game->moveText);
254
   }
255
 
256
   initializePGNGame(game);
257
}
258
 
259
void freePgnGame(PGNGame * game)
260
{
261
   resetPGNGame(game);
262
   free(game);
263
}
264
 
265
/*
266
 ******************************************************************************
267
 *
268
 *   Parser operations
269
 *
270
 ******************************************************************************
271
 */
272
 
273
static Move parsePawnMove(const Position * position, const char *moveText)
274
{
275
   Square from = NO_SQUARE, to = NO_SQUARE;
276
   Piece newPiece = NO_PIECE;
277
   int pawnStep = (position->activeColor == WHITE ? 8 : -8);
278
   size_t textLength = strlen(moveText);
279
 
280
   if (textLength < 2)
281
   {
282
      return NO_MOVE;
283
   }
284
 
285
   assert(textLength <= 6);
286
 
287
   /*
288
    * capture or push?
289
    */
290
   if (textLength >= 4 && moveText[1] == 'x')
291
   {
292
      to = getSquare(moveText[2] - 'a', moveText[3] - '1');
293
      from = (Square)
294
         (getSquare(moveText[0] - 'a', moveText[3] - '1') - pawnStep);
295
   }
296
   else
297
   {
298
      to = getSquare(moveText[0] - 'a', moveText[1] - '1');
299
      from = (Square) (to - pawnStep);
300
 
301
      if (position->piece[from] == NO_PIECE)
302
      {
303
         from = (Square) (from - pawnStep);
304
      }
305
   }
306
 
307
   /*
308
    * promotion?
309
    */
310
   if (textLength >= 4 && moveText[textLength - 2] == '=')
311
   {
312
      switch (moveText[textLength - 1])
313
      {
314
      case 'R':
315
         newPiece = WHITE_ROOK;
316
         break;
317
      case 'B':
318
         newPiece = WHITE_BISHOP;
319
         break;
320
      case 'N':
321
         newPiece = WHITE_KNIGHT;
322
         break;
323
      default:
324
         newPiece = WHITE_QUEEN;
325
      }
326
   }
327
 
328
   return getPackedMove(from, to, newPiece);
329
}
330
 
331
static Move parseCastlingMove(const Position * position, const char *moveText)
332
{
333
   const Rank rank = (position->activeColor == WHITE ? RANK_1 : RANK_8);
334
   const File file = (strcmp(moveText, "O-O") == 0 ? FILE_G : FILE_C);
335
 
336
   assert(strlen(moveText) >= 3 && strlen(moveText) <= 5);
337
 
338
   return getPackedMove(getSquare(FILE_E, rank), getSquare(file, rank),
339
                        NO_PIECE);
340
}
341
 
342
static Move parsePieceMove(const Position * position,
343
                           const char *moveText, const PieceType pieceType)
344
{
345
   Square from = NO_SQUARE, to = NO_SQUARE;
346
   Piece newPiece = NO_PIECE;
347
   size_t textLength = strlen(moveText);
348
   int fromFile = -1, fromRank = -1;
349
   size_t pointer = 1;
350
   char currentChar;
351
   Bitboard candidates;
352
 
353
   if (textLength < 3)
354
   {
355
      return NO_MOVE;
356
   }
357
 
358
   assert(strlen(moveText) <= 6);
359
 
360
   to = getSquare(moveText[textLength - 2] - 'a',
361
                  moveText[textLength - 1] - '1');
362
 
363
   while (pointer < textLength - 2)
364
   {
365
      currentChar = moveText[pointer++];
366
 
367
      if (currentChar >= 'a' && currentChar <= 'h')
368
      {
369
         fromFile = currentChar - 'a';
370
      }
371
 
372
      if (currentChar >= '1' && currentChar <= '8')
373
      {
374
         fromRank = currentChar - '1';
375
      }
376
   }
377
 
378
   if (fromFile != -1 && fromRank != -1)
379
   {
380
      from = getSquare(fromFile, fromRank);
381
   }
382
   else
383
   {
384
      candidates =
385
         getDirectAttackers(position, to, position->activeColor,
386
                            position->allPieces) &
387
         position->piecesOfType[pieceType | position->activeColor];
388
 
389
      if (fromFile != -1)
390
      {
391
         candidates &= getSquaresOfFile((File) fromFile);
392
      }
393
      else if (fromRank != -1)
394
      {
395
         candidates &= getSquaresOfRank((Rank) fromRank);
396
      }
397
 
398
      if (candidates == EMPTY_BITBOARD)
399
      {
400
         return NO_MOVE;
401
      }
402
 
403
      from = getLastSquare(&candidates);
404
 
405
      while (candidates != EMPTY_BITBOARD)
406
      {
407
         const Move move = getPackedMove(from, to, newPiece);
408
 
409
         if (moveIsLegal(position, move))
410
         {
411
            return move;
412
         }
413
 
414
         from = getLastSquare(&candidates);
415
      }
416
   }
417
 
418
   return getPackedMove(from, to, newPiece);
419
}
420
 
421
static Move parsePGNMove(const char *moveText, Position * position)
422
{
423
   char moveTextBuffer[16];
424
   int i = 0;
425
 
426
   while (i < 15 && moveText[i] != 0 && moveText[i] != '+' &&
427
          moveText[i] != '#' && moveText[i] != '!' &&
428
          moveText[i] != '?' && moveText[i] != ')' &&
429
          !isspace((int) moveText[i]))
430
   {
431
      moveTextBuffer[i] = moveText[i];
432
      i++;
433
   }
434
 
435
   moveTextBuffer[i] = 0;
436
 
437
   switch (moveTextBuffer[0])
438
   {
439
   case 'a':
440
   case 'b':
441
   case 'c':
442
   case 'd':
443
   case 'e':
444
   case 'f':
445
   case 'g':
446
   case 'h':
447
      return parsePawnMove(position, moveTextBuffer);
448
      break;
449
   case 'K':
450
      return parsePieceMove(position, moveTextBuffer, KING);
451
      break;
452
   case 'Q':
453
      return parsePieceMove(position, moveTextBuffer, QUEEN);
454
      break;
455
   case 'R':
456
      return parsePieceMove(position, moveTextBuffer, ROOK);
457
      break;
458
   case 'B':
459
      return parsePieceMove(position, moveTextBuffer, BISHOP);
460
      break;
461
   case 'N':
462
      return parsePieceMove(position, moveTextBuffer, KNIGHT);
463
      break;
464
   case 'O':
465
      return parseCastlingMove(position, moveTextBuffer);
466
      break;
467
   default:
468
 
469
      return NO_MOVE;
470
   }
471
}
472
 
473
Move interpretPGNMove(const char *moveText, PGNGame * game)
474
{
475
   Variation variation;
476
 
477
   initializeVariationFromGame(&variation, game);
478
 
479
   return parsePGNMove(moveText, &variation.singlePosition);
480
}
481
 
482
int appendMove(PGNGame * game, const Move move)
483
{
484
   Variation variation;
485
 
486
   initializeVariationFromGame(&variation, game);
487
 
488
   if (moveIsLegal(&variation.singlePosition, move))
489
   {
490
      Gamemove *newMove =
491
         getGamemove(&variation.singlePosition, game->lastMove, game);
492
 
493
      newMove->from = getFromSquare(move);
494
      newMove->to = getToSquare(move);
495
      newMove->newPiece = getNewPiece(move);
496
 
497
      if (game->lastMove == 0)
498
      {
499
         game->firstMove = game->lastMove = newMove;
500
      }
501
      else
502
      {
503
         game->lastMove->nextMove = newMove;
504
         game->lastMove = newMove;
505
      }
506
 
507
      return 0;
508
   }
509
   else
510
   {
511
      return -1;
512
   }
513
}
514
 
515
void takebackLastMove(PGNGame * game)
516
{
517
   if (game->lastMove != 0)
518
   {
519
      if (game->lastMove == game->firstMove)
520
      {
521
         game->lastMove = 0;
522
      }
523
      else
524
      {
525
         game->lastMove = game->lastMove->previousMove;
526
      }
527
   }
528
}
529
 
530
static void addAlternativeMove(Gamemove * move, Gamemove * alternative)
531
{
532
   if (move->alternativeMove != 0)
533
   {
534
      addAlternativeMove(move->alternativeMove, alternative);
535
   }
536
   else
537
   {
538
      move->alternativeMove = alternative;
539
      alternative->previousMove = move->previousMove;
540
   }
541
}
542
 
543
static Gamemove *parseMoveText(const char *pgnMoveText,
544
                               size_t * offset, const Position * position,
545
                               PGNGame * game)
546
{
547
   bool variationTerminated = FALSE;
548
   Gamemove *baseMove = 0, *lastMove = 0;
549
   Variation variation;
550
   char currentChar;
551
   const char *token;
552
   const bool debugOutput = FALSE;
553
 
554
   setBasePosition(&variation, position);
555
   prepareSearch(&variation);
556
 
557
   if (debugOutput)
558
   {
559
      logDebug("Starting pgn parsing at: >%s<\n", &pgnMoveText[*offset]);
560
   }
561
 
562
   while (variationTerminated == FALSE &&
563
          (currentChar = pgnMoveText[(*offset)++]) != '\0')
564
   {
565
      if (strchr(" \r\n0123456789.", currentChar) != 0)
566
      {
567
         continue;
568
      }
569
 
570
      token = &(pgnMoveText[*offset - 1]);
571
 
572
      if (debugOutput)
573
      {
574
         logDebug("\nCurrent token: >%s<\n", token);
575
      }
576
 
577
      if (currentChar == '{')
578
      {
579
         char *comment = getToken(token + 1, "}");
580
 
581
         if (debugOutput)
582
         {
583
            logDebug("Starting annotation: >%s<\n", comment);
584
         }
585
 
586
         (*offset) += strlen(comment);
587
 
588
         if (lastMove != 0)
589
         {
590
            lastMove->comment = comment;
591
         }
592
         else
593
         {
594
            free(comment);
595
         }
596
 
597
         continue;
598
      }
599
 
600
      if (currentChar == '(')
601
      {
602
         if (debugOutput)
603
         {
604
            logDebug("Starting subvariation.\n");
605
         }
606
 
607
         if (lastMove != 0)
608
         {
609
            addAlternativeMove(lastMove,
610
                               parseMoveText(pgnMoveText, offset,
611
                                             &lastMove->position, game));
612
         }
613
      }
614
 
615
      if (currentChar == ')')
616
      {
617
         variationTerminated = TRUE;
618
 
619
         if (debugOutput)
620
         {
621
            logDebug("Subvariation terminated.\n");
622
         }
623
      }
624
 
625
      if (currentChar == '$')
626
      {
627
         char *glyph = getToken(token, " )\r\n");
628
 
629
         if (lastMove != 0)
630
         {
631
            if (lastMove->glyphs != 0)
632
            {
633
               if (debugOutput)
634
               {
635
                  logDebug("Existing glyphs: >%s<\n", lastMove->glyphs);
636
               }
637
 
638
               lastMove->glyphs = realloc(lastMove->glyphs,
639
                                          strlen(lastMove->glyphs) +
640
                                          strlen(glyph) + 2);
641
               strcat(lastMove->glyphs, " ");
642
               strcat(lastMove->glyphs, glyph);
643
               free(glyph);
644
            }
645
            else
646
            {
647
               if (debugOutput)
648
               {
649
                  logDebug("Initializing glyphs...\n");
650
               }
651
 
652
               lastMove->glyphs = glyph;
653
            }
654
 
655
            if (debugOutput)
656
            {
657
               logDebug("glyphs: >%s<\n", lastMove->glyphs);
658
            }
659
         }
660
         else
661
         {
662
            free(glyph);
663
         }
664
      }
665
 
666
      if (strchr("KQRBNOabcdefgh", currentChar) != 0)
667
      {
668
         Move move;
669
 
670
         /*dumpPosition(variation->currentPosition); */
671
 
672
         if (debugOutput)
673
         {
674
            logDebug("Move token: >%s<\n", token);
675
         }
676
 
677
         move = parsePGNMove(token, &variation.singlePosition);
678
 
679
         if (moveIsLegal(&variation.singlePosition, move))
680
         {
681
            Gamemove *newMove =
682
               getGamemove(&variation.singlePosition, lastMove, game);
683
 
684
            newMove->from = getFromSquare(move);
685
            newMove->to = getToSquare(move);
686
            newMove->newPiece = getNewPiece(move);
687
 
688
            if (lastMove == 0)
689
            {
690
               baseMove = newMove;
691
            }
692
            else
693
            {
694
               lastMove->nextMove = newMove;
695
            }
696
 
697
            lastMove = newMove;
698
            makeMove(&variation, move);
699
            setBasePosition(&variation, &variation.singlePosition);
700
         }
701
         else
702
         {
703
            logDebug("### Illegal move: %s\n", token);
704
            dumpMove(move);
705
            dumpPosition(&variation.singlePosition);
706
            exit(-1);
707
 
708
            break;
709
         }
710
 
711
         while (isspace((int) pgnMoveText[*offset]) == FALSE &&
712
                pgnMoveText[*offset] != ')')
713
         {
714
            (*offset)++;
715
         }
716
      }
717
   }
718
 
719
   return baseMove;
720
}
721
 
722
static Gamemove *parseMoveSection(const char *pgnMoveText,
723
                                  const Position * position, PGNGame * game)
724
{
725
   size_t offset = 0;
726
 
727
   return parseMoveText(pgnMoveText, &offset, position, game);
728
}
729
 
730
static void parseRoasterValue(const char *pgn, const char *name,
731
                              char value[PGN_ROASTERLINE_SIZE])
732
{
733
   char *nameBegin, *valueBegin, *valueEnd;
734
   char buffer[2 * PGN_ROASTERLINE_SIZE];
735
   size_t valueLength;
736
 
737
   strcpy(value, "");
738
   strcpy(buffer, "[");
739
   strcat(buffer, name);
740
 
741
   if ((nameBegin = strstr(pgn, buffer)) == 0)
742
   {
743
      return;
744
   }
745
 
746
   if ((valueBegin = strstr(nameBegin, "\"")) == 0)
747
   {
748
      return;
749
   }
750
 
751
   if ((valueEnd = strstr(valueBegin, "\"]")) == 0)
752
   {
753
      return;
754
   }
755
 
756
   valueLength = min(PGN_ROASTERLINE_SIZE, valueEnd - valueBegin - 1);
757
   memcpy(value, valueBegin + 1, valueLength);
758
   value[valueLength] = '\0';
759
}
760
 
761
static void parseRoasterValues(const char *pgn, PGNGame * pgngame)
762
{
763
   char *moves;
764
   Variation variation;
765
 
766
   parseRoasterValue(pgn, "Event", pgngame->event);
767
   parseRoasterValue(pgn, "Site", pgngame->site);
768
   parseRoasterValue(pgn, "Date", pgngame->date);
769
   parseRoasterValue(pgn, "Round", pgngame->round);
770
   parseRoasterValue(pgn, "White", pgngame->white);
771
   parseRoasterValue(pgn, "Black", pgngame->black);
772
   parseRoasterValue(pgn, "Result", pgngame->result);
773
   parseRoasterValue(pgn, "SetUp", pgngame->setup);
774
   parseRoasterValue(pgn, "FEN", pgngame->fen);
775
   parseRoasterValue(pgn, "WhiteTitle", pgngame->whiteTitle);
776
   parseRoasterValue(pgn, "BlackTitle", pgngame->blackTitle);
777
   parseRoasterValue(pgn, "WhiteElo", pgngame->whiteElo);
778
   parseRoasterValue(pgn, "BlackElo", pgngame->blackElo);
779
   parseRoasterValue(pgn, "ECO", pgngame->eco);
780
   parseRoasterValue(pgn, "NIC", pgngame->nic);
781
   parseRoasterValue(pgn, "TimeControl", pgngame->timeControl);
782
   parseRoasterValue(pgn, "Termination", pgngame->termination);
783
   pgngame->moveText = 0;
784
   pgngame->firstMove = 0;
785
 
786
   initializeVariationFromGame(&variation, pgngame);
787
 
788
   if ((moves = strstr(pgn, "\n\n")) == 0)
789
   {
790
      moves = strstr(pgn, "\r\n\r\n");
791
   }
792
 
793
   if (moves != 0)
794
   {
795
      if ((pgngame->moveText = malloc(strlen(moves) + 1)) != 0)
796
      {
797
         strcpy(pgngame->moveText, moves);
798
         trim(pgngame->moveText);
799
 
800
         pgngame->firstMove =
801
            parseMoveSection(pgngame->moveText, &variation.singlePosition,
802
                             pgngame);
803
      }
804
   }
805
}
806
 
807
PGNGame *getGame(PGNFile * pgnfile, int number)
808
{
809
   PGNGame *pgngame;
810
   char *gameText = getGameText(pgnfile, number);
811
 
812
   if (gameText == 0)
813
   {
814
      return 0;
815
   }
816
 
817
   if ((pgngame = (PGNGame *) malloc(sizeof(PGNGame))) == NULL)
818
   {
819
      return 0;
820
   }
821
 
822
   initializePGNGame(pgngame);
823
   parseRoasterValues(gameText, pgngame);
824
   free(gameText);
825
 
826
   return pgngame;
827
}
828
 
829
/*
830
 ******************************************************************************
831
 *
832
 *   Generator operations
833
 *
834
 ******************************************************************************
835
 */
836
 
837
#define SAME_PIECE 1
838
#define SAME_FILE 2
839
#define SAME_RANK 4
840
 
841
static int examineAlternativeMoves(Variation * variation, const Move move)
842
{
843
   const Square from = getFromSquare(move);
844
   const Square to = getToSquare(move);
845
   const Piece newPiece = getNewPiece(move);
846
   int result = 0;
847
   Position *position = &variation->singlePosition;
848
   Piece piece = position->piece[from];
849
   Square square;
850
   Bitboard candidates =
851
      getDirectAttackers(position, to, position->activeColor,
852
                         position->allPieces) & position->piecesOfType[piece];
853
 
854
   clearSquare(candidates, from);
855
 
856
   ITERATE_BITBOARD(&candidates, square)
857
   {
858
      const Move tmp = getPackedMove(square, to, newPiece);
859
 
860
      if (moveIsLegal(position, tmp))
861
      {
862
         result |= SAME_PIECE;
863
 
864
         if (file(square) == file(from))
865
         {
866
            result |= SAME_FILE;
867
         }
868
 
869
         if (rank(square) == rank(from))
870
         {
871
            result |= SAME_RANK;
872
         }
873
      }
874
   }
875
 
876
   return result;
877
}
878
 
879
void generateMoveText(Variation * variation, const Move move, char *pgnMove)
880
{
881
   Position *position = &variation->singlePosition;
882
   const char *castlings[] = { "O-O", "O-O-O" };
883
   const Square from = getFromSquare(move);
884
   const Square to = getToSquare(move);
885
   const Piece newPiece = getNewPiece(move);
886
 
887
   Piece movingPiece = position->piece[from];
888
   char pieceSign[2], origin[3], destination[6], captureSign[2];
889
   char checkSign[2], promotionSign[3];
890
   int ambiguityCheckResult;
891
 
892
   getSquareName(to, destination);
893
   pieceSign[0] = origin[0] = captureSign[0] = checkSign[0] = 0;
894
   promotionSign[0] = 0;
895
 
896
   if (!moveIsLegal(position, move))
897
   {
898
      getSquareName(from, origin);
899
      sprintf(pgnMove, "(illegal move: %s-%s)", origin, destination);
900
 
901
      return;
902
   }
903
   else
904
   {
905
      makeMove(variation, move);
906
 
907
      if (activeKingIsSafe(&variation->singlePosition) == FALSE)
908
      {
909
         Movelist legalMoves;
910
 
911
         getLegalMoves(variation, &legalMoves);
912
 
913
         if (legalMoves.numberOfMoves == 0)
914
         {
915
            strcpy(checkSign, "#");
916
         }
917
         else
918
         {
919
            strcpy(checkSign, "+");
920
         }
921
      }
922
 
923
      unmakeLastMove(variation);
924
   }
925
 
926
   if (pieceType(movingPiece) == PAWN)
927
   {
928
      if (file(from) != file(to))
929
      {
930
         strcpy(captureSign, "x");
931
         sprintf(origin, "%c", fileName(file(from)));
932
      }
933
 
934
      if (newPiece != NO_PIECE)
935
      {
936
         sprintf(promotionSign, "=%c", pieceName[newPiece]);
937
      }
938
   }
939
   else
940
   {
941
      if (pieceType(movingPiece) == KING && _distance[from][to] > 1)
942
      {
943
         strcpy(destination, castlings[file(to) == FILE_G ? 0 : 1]);
944
      }
945
      else
946
      {
947
         sprintf(pieceSign, "%c", pieceName[pieceType(movingPiece)]);
948
 
949
         if (position->piece[to] != NO_PIECE)
950
         {
951
            strcpy(captureSign, "x");
952
         }
953
 
954
         ambiguityCheckResult = examineAlternativeMoves(variation, move);
955
 
956
         if (ambiguityCheckResult & SAME_PIECE)
957
         {
958
            switch (ambiguityCheckResult)
959
            {
960
            case SAME_PIECE | SAME_FILE | SAME_RANK:
961
               getSquareName(from, origin);
962
               break;
963
 
964
            case SAME_PIECE | SAME_FILE:
965
               sprintf(origin, "%c", rankName(rank(from)));
966
               break;
967
 
968
            default:
969
               sprintf(origin, "%c", fileName(file(from)));
970
            }
971
         }
972
      }
973
   }
974
 
975
   sprintf(pgnMove, "%s%s%s%s%s%s", pieceSign, origin, captureSign,
976
           destination, promotionSign, checkSign);
977
}
978
 
979
static char *generateMoveSection(Gamemove * gamemove, const char *result)
980
{
981
   String moveText = getEmptyString();
982
   char moveBuffer[64], *temp;
983
   Variation variation;
984
   bool restart = TRUE;
985
 
986
   initializeVariation(&variation, FEN_GAMESTART);
987
 
988
   while (gamemove != 0)
989
   {
990
      if (restart == TRUE && gamemove->position.activeColor == BLACK)
991
      {
992
         appendToString(&moveText, " %d...", gamemove->position.moveNumber);
993
      }
994
 
995
      restart = FALSE;
996
 
997
      setBasePosition(&variation, &gamemove->position);
998
      generateMoveText(&variation,
999
                       getPackedMove(gamemove->from, gamemove->to,
1000
                                     gamemove->newPiece), moveBuffer);
1001
 
1002
      if (gamemove->position.activeColor == WHITE)
1003
      {
1004
         appendToString(&moveText, " %d. %s", gamemove->position.moveNumber,
1005
                        moveBuffer);
1006
      }
1007
      else
1008
      {
1009
         appendToString(&moveText, " %s", moveBuffer);
1010
      }
1011
 
1012
      if (gamemove->glyphs != 0)
1013
      {
1014
         appendToString(&moveText, " %s", gamemove->glyphs);
1015
      }
1016
 
1017
      if (gamemove->comment != 0)
1018
      {
1019
         appendToString(&moveText, " {%s}", gamemove->comment);
1020
      }
1021
 
1022
      if (gamemove->alternativeMove != 0)
1023
      {
1024
         temp = generateMoveSection(gamemove->alternativeMove, "");
1025
         appendToString(&moveText, " (%s)", temp);
1026
         free(temp);
1027
 
1028
         restart = TRUE;
1029
      }
1030
 
1031
      gamemove = gamemove->nextMove;
1032
   }
1033
 
1034
   if (result[0] != '\0')
1035
   {
1036
      appendToString(&moveText, " %s", result);
1037
   }
1038
 
1039
   trim(moveText.buffer);
1040
   breakLines(moveText.buffer, 79);
1041
 
1042
   return moveText.buffer;
1043
}
1044
 
1045
static char *generateRoasterLine(const char *tag, const char *tagValue,
1046
                                 char buffer[PGN_ROASTERLINE_SIZE])
1047
{
1048
   if (tagValue[0] != '\0')
1049
   {
1050
      sprintf(buffer, "[%s \"%s\"]\n", tag, tagValue);
1051
   }
1052
   else
1053
   {
1054
      buffer[0] = '\0';
1055
   }
1056
 
1057
   return buffer;
1058
}
1059
 
1060
static char *generateRoaster(const PGNGame * pgngame)
1061
{
1062
   char tagbuffer[PGN_ROASTERLINE_SIZE];
1063
   String string = getEmptyString();
1064
 
1065
   appendToString(&string,
1066
                  generateRoasterLine("Event", pgngame->event, tagbuffer));
1067
   appendToString(&string,
1068
                  generateRoasterLine("Site", pgngame->site, tagbuffer));
1069
   appendToString(&string,
1070
                  generateRoasterLine("Date", pgngame->date, tagbuffer));
1071
   appendToString(&string,
1072
                  generateRoasterLine("Round", pgngame->round, tagbuffer));
1073
   appendToString(&string,
1074
                  generateRoasterLine("White", pgngame->white, tagbuffer));
1075
   appendToString(&string,
1076
                  generateRoasterLine("Black", pgngame->black, tagbuffer));
1077
   appendToString(&string,
1078
                  generateRoasterLine("Result", pgngame->result, tagbuffer));
1079
   appendToString(&string,
1080
                  generateRoasterLine("SetUp", pgngame->setup, tagbuffer));
1081
   appendToString(&string,
1082
                  generateRoasterLine("FEN", pgngame->fen, tagbuffer));
1083
   appendToString(&string,
1084
                  generateRoasterLine("WhiteTitle", pgngame->whiteTitle,
1085
                                      tagbuffer));
1086
   appendToString(&string,
1087
                  generateRoasterLine("BlackTitle", pgngame->blackTitle,
1088
                                      tagbuffer));
1089
   appendToString(&string,
1090
                  generateRoasterLine("WhiteElo", pgngame->whiteElo,
1091
                                      tagbuffer));
1092
   appendToString(&string,
1093
                  generateRoasterLine("BlackElo", pgngame->blackElo,
1094
                                      tagbuffer));
1095
   appendToString(&string,
1096
                  generateRoasterLine("ECO", pgngame->eco, tagbuffer));
1097
   appendToString(&string,
1098
                  generateRoasterLine("NIC", pgngame->nic, tagbuffer));
1099
   appendToString(&string,
1100
                  generateRoasterLine("TimeControl", pgngame->timeControl,
1101
                                      tagbuffer));
1102
   appendToString(&string,
1103
                  generateRoasterLine("Termination", pgngame->termination,
1104
                                      tagbuffer));
1105
 
1106
   return string.buffer;
1107
}
1108
 
1109
char *generatePgn(PGNGame * game)
1110
{
1111
   String string = getEmptyString();
1112
   char *roaster = generateRoaster(game);
1113
   char *moveText = generateMoveSection(game->firstMove, game->result);
1114
 
1115
   appendToString(&string, "%s\n%s", roaster, moveText);
1116
   free(roaster);
1117
   free(moveText);
1118
 
1119
   return string.buffer;
1120
}
1121
 
1122
void initializeVariationFromGame(Variation * variation, PGNGame * game)
1123
{
1124
   if (game->lastMove != 0)
1125
   {
1126
      Gamemove *currentMove = game->lastMove;
1127
      int i = POSITION_HISTORY_OFFSET - 1;
1128
 
1129
      setBasePosition(variation, &(game->lastMove->position));
1130
      makeMove(variation, gameMove2Move(game->lastMove));
1131
 
1132
      if (variation->singlePosition.activeColor == WHITE)
1133
      {
1134
         variation->singlePosition.moveNumber++;
1135
      }
1136
 
1137
      setBasePosition(variation, &variation->singlePosition);
1138
      prepareSearch(variation);
1139
 
1140
      while (i >= 0 && currentMove != 0)
1141
      {
1142
         variation->positionHistory[i--] = currentMove->position.hashKey;
1143
         currentMove = currentMove->previousMove;
1144
      }
1145
   }
1146
   else
1147
   {
1148
      if (strcmp(game->setup, "") != 0)
1149
      {
1150
         initializeVariation(variation, game->fen);
1151
      }
1152
      else
1153
      {
1154
         initializeVariation(variation, FEN_GAMESTART);
1155
      }
1156
   }
1157
}
1158
 
1159
void initializeGameFromVariation(const Variation * variation, PGNGame * game,
1160
                                 bool copyPv)
1161
{
1162
   const PrincipalVariation *pv = &variation->pv[0];
1163
 
1164
   resetPGNGame(game);
1165
   getFen(&variation->startPosition, game->fen);
1166
   strcpy(game->setup, "1");
1167
 
1168
   if (copyPv != FALSE)
1169
   {
1170
      int i = 0;
1171
 
1172
      for (i = 0; i < min(8, pv->length); i++)
1173
      {
1174
         Move move = (Move) pv->move[i];
1175
 
1176
         if (appendMove(game, move) != 0)
1177
         {
1178
            break;
1179
         }
1180
      }
1181
   }
1182
}
1183
 
1184
char *getPrincipalVariation(const Variation * variation)
1185
{
1186
   PGNGame game;
1187
   char *pv;
1188
 
1189
   initializePGNGame(&game);
1190
   initializeGameFromVariation(variation, &game, TRUE);
1191
   pv = generateMoveSection(game.firstMove, "");
1192
   trim(pv);
1193
   resetPGNGame(&game);
1194
 
1195
   return pv;
1196
}
1197
 
1198
/*
1199
 ******************************************************************************
1200
 *
1201
 *   Module initialization and testing
1202
 *
1203
 ******************************************************************************
1204
 */
1205
 
1206
int initializeModulePgn()
1207
{
1208
   pieceName[WHITE_KING] = 'K';
1209
   pieceName[WHITE_QUEEN] = 'Q';
1210
   pieceName[WHITE_ROOK] = 'R';
1211
   pieceName[WHITE_BISHOP] = 'B';
1212
   pieceName[WHITE_KNIGHT] = 'N';
1213
   pieceName[WHITE_PAWN] = 'P';
1214
   pieceName[BLACK_KING] = 'K';
1215
   pieceName[BLACK_QUEEN] = 'Q';
1216
   pieceName[BLACK_ROOK] = 'R';
1217
   pieceName[BLACK_BISHOP] = 'B';
1218
   pieceName[BLACK_KNIGHT] = 'N';
1219
   pieceName[BLACK_PAWN] = 'P';
1220
 
1221
   return 0;
1222
}
1223
 
1224
static int testGeneration()
1225
{
1226
   char *p1 = "r2qkb1r/ppp2ppp/3p4/5b2/3PN3/n4N2/PPP1QPPP/R3K2R w KQkq - 0 1";
1227
   char *p2 = "r3k2r/n3nppp/3n2P1/8/1q1q4/2B5/1q1pRPPP/5RK1 b kq - 0 1";
1228
   Variation variation;
1229
   char pgnMove[64];
1230
 
1231
   initializeVariation(&variation, p1);
1232
 
1233
   generateMoveText(&variation, getPackedMove(D4, D5, NO_PIECE), pgnMove);
1234
   assert(strcmp("d5", pgnMove) == 0);
1235
 
1236
   generateMoveText(&variation, getPackedMove(E1, G1, NO_PIECE), pgnMove);
1237
   assert(strcmp("O-O", pgnMove) == 0);
1238
 
1239
   generateMoveText(&variation, getPackedMove(E1, C1, NO_PIECE), pgnMove);
1240
   assert(strcmp("O-O-O", pgnMove) == 0);
1241
 
1242
   generateMoveText(&variation, getPackedMove(E4, D6, NO_PIECE), pgnMove);
1243
   assert(strcmp("Nxd6+", pgnMove) == 0);
1244
 
1245
   generateMoveText(&variation, getPackedMove(E4, F6, NO_PIECE), pgnMove);
1246
   assert(strcmp("Nf6#", pgnMove) == 0);
1247
 
1248
   generateMoveText(&variation, getPackedMove(F3, G5, NO_PIECE), pgnMove);
1249
   assert(strcmp("Nfg5", pgnMove) == 0);
1250
 
1251
   generateMoveText(&variation, getPackedMove(E4, G5, NO_PIECE), pgnMove);
1252
   assert(strcmp("Neg5+", pgnMove) == 0);
1253
 
1254
   generateMoveText(&variation, getPackedMove(B2, A3, NO_PIECE), pgnMove);
1255
   assert(strcmp("bxa3", pgnMove) == 0);
1256
 
1257
   initializeVariation(&variation, p2);
1258
 
1259
   generateMoveText(&variation, getPackedMove(E7, F5, NO_PIECE), pgnMove);
1260
   assert(strcmp("(illegal move: e7-f5)", pgnMove) == 0);
1261
 
1262
   generateMoveText(&variation, getPackedMove(D2, D1, WHITE_BISHOP), pgnMove);
1263
   assert(strcmp("d1=B", pgnMove) == 0);
1264
 
1265
   generateMoveText(&variation, getPackedMove(B4, C3, NO_PIECE), pgnMove);
1266
   assert(strcmp("Qb4xc3", pgnMove) == 0);
1267
 
1268
   generateMoveText(&variation, getPackedMove(B2, C3, NO_PIECE), pgnMove);
1269
   assert(strcmp("Q2xc3", pgnMove) == 0);
1270
 
1271
   generateMoveText(&variation, getPackedMove(D4, C3, NO_PIECE), pgnMove);
1272
   assert(strcmp("Qdxc3", pgnMove) == 0);
1273
 
1274
   generateMoveText(&variation, getPackedMove(D6, F5, NO_PIECE), pgnMove);
1275
   assert(strcmp("Nf5", pgnMove) == 0);
1276
 
1277
   generateMoveText(&variation, getPackedMove(D6, C8, NO_PIECE), pgnMove);
1278
   assert(strcmp("Ndc8", pgnMove) == 0);
1279
 
1280
   return 0;
1281
}
1282
 
1283
static int testParsing()
1284
{
1285
   char *p1 = "r2qkb1r/ppp2ppp/3p4/5b2/3PN3/n4N2/PPP1QPPP/R3K2R w KQkq - 0 1";
1286
   char *p2 = "r3k2r/n3nppp/3n2P1/8/1q1q4/2B5/1q1pRPPP/5RK1 b kq - 0 1";
1287
   Variation variation;
1288
 
1289
   initializeVariation(&variation, p1);
1290
 
1291
   assert(parsePGNMove("d5", &variation.singlePosition) ==
1292
          getPackedMove(D4, D5, NO_PIECE));
1293
 
1294
   assert(parsePGNMove("b4", &variation.singlePosition) ==
1295
          getPackedMove(B2, B4, NO_PIECE));
1296
 
1297
   assert(parsePGNMove("bxa3", &variation.singlePosition) ==
1298
          getPackedMove(B2, A3, NO_PIECE));
1299
 
1300
   assert(parsePGNMove("O-O", &variation.singlePosition) ==
1301
          getPackedMove(E1, G1, NO_PIECE));
1302
 
1303
   assert(parsePGNMove("O-O-O", &variation.singlePosition) ==
1304
          getPackedMove(E1, C1, NO_PIECE));
1305
 
1306
   assert(parsePGNMove("Ng3+", &variation.singlePosition) ==
1307
          getPackedMove(E4, G3, NO_PIECE));
1308
 
1309
   assert(parsePGNMove("Nf6#", &variation.singlePosition) ==
1310
          getPackedMove(E4, F6, NO_PIECE));
1311
 
1312
   assert(parsePGNMove("Nfg5", &variation.singlePosition) ==
1313
          getPackedMove(F3, G5, NO_PIECE));
1314
 
1315
   initializeVariation(&variation, p2);
1316
 
1317
   assert(parsePGNMove("h6", &variation.singlePosition) ==
1318
          getPackedMove(H7, H6, NO_PIECE));
1319
 
1320
   assert(parsePGNMove("f5", &variation.singlePosition) ==
1321
          getPackedMove(F7, F5, NO_PIECE));
1322
 
1323
   assert(parsePGNMove("fxg6", &variation.singlePosition) ==
1324
          getPackedMove(F7, G6, NO_PIECE));
1325
 
1326
   assert(parsePGNMove("d1=B", &variation.singlePosition) ==
1327
          getPackedMove(D2, D1, BISHOP));
1328
 
1329
   assert(parsePGNMove("O-O", &variation.singlePosition) ==
1330
          getPackedMove(E8, G8, NO_PIECE));
1331
 
1332
   assert(parsePGNMove("O-O-O", &variation.singlePosition) ==
1333
          getPackedMove(E8, C8, NO_PIECE));
1334
 
1335
   assert(parsePGNMove("Nf5", &variation.singlePosition) ==
1336
          getPackedMove(D6, F5, NO_PIECE));
1337
 
1338
   assert(parsePGNMove("Q2xc3", &variation.singlePosition) ==
1339
          getPackedMove(B2, C3, NO_PIECE));
1340
 
1341
   assert(parsePGNMove("Qdxc3", &variation.singlePosition) ==
1342
          getPackedMove(D4, C3, NO_PIECE));
1343
 
1344
   assert(parsePGNMove("Qb4xc3", &variation.singlePosition) ==
1345
          getPackedMove(B4, C3, NO_PIECE));
1346
 
1347
   return 0;
1348
}
1349
 
1350
static int testLoading()
1351
{
1352
   PGNFile pgnfile;
1353
   PGNGame *game, game2;
1354
   char *gameText, *generatedGameText;
1355
 
1356
   pgnfile.numGames = 0;
1357
   pgnfile.index = 0;
1358
   pgnfile.file = 0;
1359
 
1360
   assert(openPGNFile(&pgnfile, "test.pgn") == 0);
1361
   game = getGame(&pgnfile, 1);
1362
   assert(game != 0);
1363
 
1364
   gameText = generatePgn(game);
1365
   initializePGNGame(&game2);
1366
   parseRoasterValues(gameText, &game2);
1367
   generatedGameText = generatePgn(&game2);
1368
   /* logDebug("\n>%s<\n", generatedGameText); */
1369
   assert(strcmp(gameText, generatedGameText) == 0);
1370
 
1371
   free(generatedGameText);
1372
   freePgnGame(game);
1373
   resetPGNGame(&game2);
1374
   free(gameText);
1375
   closePGNFile(&pgnfile);
1376
 
1377
   return 0;
1378
}
1379
 
1380
int testModulePgn()
1381
{
1382
   int result;
1383
 
1384
   if ((result = testLoading()) != 0)
1385
   {
1386
      return result;
1387
   }
1388
 
1389
   if ((result = testGeneration()) != 0)
1390
   {
1391
      return result;
1392
   }
1393
 
1394
   if ((result = testParsing()) != 0)
1395
   {
1396
      return result;
1397
   }
1398
 
1399
   return 0;
1400
}