Update copyright info for 2008 (minishogi patches) and recent developments.
[gnushogi.git] / gnushogi / book.c
1 /*
2  * FILE: book.c
3  *
4  * ----------------------------------------------------------------------
5  * Copyright (c) 1993, 1994, 1995 Matthias Mutz
6  * Copyright (c) 1999 Michael Vanier and the Free Software Foundation
7  * Copyright (c) 2008, 2013, 2014 Yann Dirson and the Free Software Foundation
8  *
9  * GNU SHOGI is based on GNU CHESS
10  *
11  * Copyright (c) 1988, 1989, 1990 John Stanback
12  * Copyright (c) 1992 Free Software Foundation
13  *
14  * This file is part of GNU SHOGI.
15  *
16  * GNU Shogi is free software; you can redistribute it and/or modify it
17  * under the terms of the GNU General Public License as published by the
18  * Free Software Foundation; either version 3 of the License,
19  * or (at your option) any later version.
20  *
21  * GNU Shogi is distributed in the hope that it will be useful, but WITHOUT
22  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
24  * for more details.
25  *
26  * You should have received a copy of the GNU General Public License along
27  * with GNU Shogi; see the file COPYING. If not, see
28  * <http://www.gnu.org/licenses/>.
29  * ----------------------------------------------------------------------
30  *
31  */
32
33 #include "gnushogi.h"
34
35 #define O_BINARY 0
36
37 #if HAVE_UNISTD_H
38 /* Declarations of read(), write(), close(), and lseek(). */
39 #include <unistd.h>
40 #endif
41
42 #if HAVE_FCNTL_H
43 #include <fcntl.h>
44 #endif
45
46 #include "book.h"
47
48 unsigned booksize = BOOKSIZE;
49 unsigned short bookmaxply = BOOKMAXPLY;
50 unsigned bookcount = 0;
51
52 #ifdef BOOK
53 char *bookfile = BOOK;
54 #else
55 char *bookfile = NULL;
56 #endif
57
58 #ifdef BINBOOK
59 char *binbookfile = BINBOOK;
60 #else
61 char *binbookfile = NULL;
62 #endif
63
64 static char bmvstr[3][7];
65
66 static ULONG bhashbd;
67 static ULONG bhashkey;
68
69
70 /*
71  * Balgbr(f, t, flag)
72  *
73  * Generate move strings in different formats.
74  */
75
76 void
77 Balgbr(short f, short t, short flag)
78 {
79     short promoted = false;
80
81     if ((f & 0x80) != 0)
82     {
83         f &= 0x7f;
84         promoted = true;
85     }
86
87     if (f > NO_SQUARES)
88     {
89         short piece;
90         piece = f - NO_SQUARES;
91
92         if (f > (NO_SQUARES + NO_PIECES))
93             piece -= NO_PIECES;
94
95         flag = (dropmask | piece);
96     }
97
98     if ((t & 0x80) != 0)
99     {
100         flag |= promote;
101         t &= 0x7f;
102     }
103
104     if ((f == t) && ((f != 0) || (t != 0)))
105     {
106         /*
107          * error in algbr: FROM=TO=t
108          */
109
110         bmvstr[0][0] = bmvstr[1][0] = bmvstr[2][0] = '\0';
111     }
112     else
113     {
114         if ((flag & dropmask) != 0)
115         {
116             /* bmvstr[0]: P*3c bmvstr[1]: P'3c */
117             short piece = flag & pmask;
118             bmvstr[0][0] = pxx[piece];
119             bmvstr[0][1] = '*';
120             bmvstr[0][2] = cxx[column(t)];
121             bmvstr[0][3] = rxx[row(t)];
122             bmvstr[0][4] = bmvstr[2][0] = '\0';
123             strcpy(bmvstr[1], bmvstr[0]);
124             bmvstr[1][1] = '\'';
125         }
126         else
127         {
128             if ((f != 0) || (t != 0))
129             {
130                 /* algebraic notation */
131                 /* bmvstr[0]: 7g7f bmvstr[1]:
132                  * (+)P7g7f(+) bmvstr[2]: (+)P7f(+) */
133                 bmvstr[0][0] = cxx[column(f)];
134                 bmvstr[0][1] = rxx[row(f)];
135                 bmvstr[0][2] = cxx[column(t)];
136                 bmvstr[0][3] = rxx[row(t)];
137                 bmvstr[0][4] = '\0';
138
139                 if (promoted)
140                 {
141                     bmvstr[1][0] = bmvstr[2][0] = '+';
142                     bmvstr[1][1] = bmvstr[2][1] = pxx[board[f]];
143                     strcpy(&bmvstr[1][2], &bmvstr[0][0]);
144                     strcpy(&bmvstr[2][2], &bmvstr[0][2]);
145                 }
146                 else
147                 {
148                     bmvstr[1][0] = bmvstr[2][0] = pxx[board[f]];
149                     strcpy(&bmvstr[1][1], &bmvstr[0][0]);
150                     strcpy(&bmvstr[2][1], &bmvstr[0][2]);
151                 }
152
153                 if (flag & promote)
154                 {
155                     strcat(bmvstr[0], "+");
156                     strcat(bmvstr[1], "+");
157                     strcat(bmvstr[2], "+");
158                 }
159             }
160             else
161             {
162                 bmvstr[0][0] = bmvstr[1][0] = bmvstr[2][0] = '\0';
163             }
164         }
165     }
166 }
167
168
169
170
171 #ifndef QUIETBOOKGEN
172 void
173 bkdisplay(char *s, int cnt, int moveno)
174 {
175     static short pnt;
176     struct leaf  *node;
177     int r, c, l;
178
179     pnt = TrPnt[2];
180     printf("matches = %d\n", cnt);
181     printf("inout move is :%s: move number %d side %s\n",
182             s, moveno / 2 + 1, (moveno & 1) ? "white" : "black");
183
184 #ifndef SEMIQUIETBOOKGEN
185     printf("legal moves are \n");
186
187     while (pnt < TrPnt[3])
188     {
189         node = &Tree[pnt++];
190
191         if (is_promoted[board[node->f]] )
192             Balgbr(node->f | 0x80, node->t, (short) node->flags);
193         else
194             Balgbr(node->f, node->t, (short) node->flags);
195
196         printf("%s %s %s\n",
197                bmvstr[0], bmvstr[1], bmvstr[2]);
198     }
199
200     printf("\n current board is\n");
201
202     for (r = (NO_ROWS - 1); r >= 0; r--)
203     {
204         for (c = 0; c <= (NO_COLS - 1); c++)
205         {
206             char pc;
207
208             l = locn(r, c);
209             pc = (is_promoted[board[l]] ? '+' : ' ');
210
211             if (color[l] == neutral)
212                 printf(" -");
213             else if (color[l] == black)
214                 printf("%c%c", pc, qxx[board[l]]);
215             else
216                 printf("%c%c", pc, pxx[board[l]]);
217         }
218
219         printf("\n");
220     }
221
222     printf("\n");
223     {
224         short color;
225
226         for (color = black; color <= white; color++)
227         {
228             short piece, c;
229
230             printf((color == black) ? "black " : "white ");
231
232             for (piece = pawn; piece <= king; piece++)
233             {
234                 if ((c = Captured[color][piece]))
235                     printf("%i%c ", c, pxx[piece]);
236             }
237
238             printf("\n");
239         };
240     }
241 #endif /* SEMIQUIETBOOKGEN */
242 }
243
244 #endif /* QUIETBOOKGEN */
245
246
247
248 /*
249  * BVerifyMove(s, mv, moveno)
250  *
251  * Compare the string 's' to the list of legal moves available for the
252  * opponent. If a match is found, make the move on the board.
253  */
254
255 int
256 BVerifyMove(char *s, unsigned short *mv, int moveno)
257 {
258     static short pnt, tempb, tempc, tempsf, tempst, cnt;
259     static struct leaf xnode;
260     struct leaf  *node;
261
262     *mv = 0;
263     cnt = 0;
264     MoveList(opponent, 2, -2, true);
265     pnt = TrPnt[2];
266
267     while (pnt < TrPnt[3])
268     {
269         node = &Tree[pnt++];
270
271         if (is_promoted[board[node->f]] )
272             Balgbr(node->f | 0x80, node->t, (short) node->flags);
273         else
274             Balgbr(node->f, node->t, (short) node->flags);
275
276         if (strcmp(s, bmvstr[0]) == 0 || strcmp(s, bmvstr[1]) == 0 ||
277             strcmp(s, bmvstr[2]) == 0)
278         {
279             cnt++;
280             xnode = *node;
281         }
282     }
283
284     if (cnt == 1)
285     {
286         short blockable;
287
288         MakeMove(opponent, &xnode, &tempb,
289                  &tempc, &tempsf, &tempst, &INCscore);
290
291         if (SqAttacked(PieceList[opponent][0], computer, &blockable))
292         {
293             UnmakeMove(opponent, &xnode, &tempb, &tempc, &tempsf, &tempst);
294             /* Illegal move in check */
295 #if !defined QUIETBOOKGEN
296             puts("Illegal move (in check) %s");
297             bkdisplay(s, cnt, moveno);
298 #endif
299             return false;
300         }
301         else
302         {
303             *mv = (xnode.f << 8) | xnode.t;
304
305             if (is_promoted[board[xnode.t]] )
306                 Balgbr(xnode.f | 0x80, xnode.t, false);
307             else
308                 Balgbr(xnode.f, xnode.t, false);
309
310             return true;
311         }
312     }
313
314     /* Illegal move */
315 #if !defined QUIETBOOKGEN
316     printf("Illegal move (no match) %s\n", s);
317     bkdisplay(s, cnt, moveno);
318 #endif
319     return false;
320 }
321
322
323
324
325 /*
326  * RESET()
327  *
328  * Reset the board and other variables to start a new game.
329  *
330  */
331
332 void
333 RESET(void)
334 {
335     short l;
336
337     flag.illegal = flag.mate = flag.post = flag.quit
338         = flag.reverse = flag.bothsides = flag.onemove = flag.force
339         = false;
340
341     flag.material = flag.coords = flag.hash = flag.easy
342         = flag.beep = flag.rcptr
343         = true;
344
345     flag.stars = flag.shade = flag.back = flag.musttimeout = false;
346     flag.gamein = false;
347     GenCnt   = 0;
348     GameCnt  = 0;
349     CptrFlag[0] = TesujiFlag[0] = false;
350     opponent = black;
351     computer = white;
352
353     for (l = 0; l < NO_SQUARES; l++)
354     {
355         board[l]   = Stboard[l];
356         color[l]   = Stcolor[l];
357         Mvboard[l] = 0;
358     }
359
360     ClearCaptured();
361     InitializeStats();
362     hashbd = hashkey = 0;
363 }
364
365
366
367 static
368 int
369 Vparse (FILE * fd, USHORT *mv, USHORT *flags, USHORT side, int moveno)
370 {
371     int c, i;
372     char s[255];
373
374     *flags = 0;
375
376     while (true)
377     {
378         while (((c = getc(fd)) == ' ')
379                || (c == '!') || (c == '/') || (c == '\n'));
380
381         if (c == '(')
382         {
383             /* amount of time spent for the last move */
384             while (((c = getc(fd)) != ')') && (c != EOF));
385
386             if (c == ')')
387             {
388                 while (((c = getc(fd)) == ' ') || (c == '\n'));
389             }
390         }
391
392         if (c == '[')
393         {
394             /* comment for the actual game */
395             while (((c = getc(fd))) != ']' && (c != EOF));
396
397             if (c == ']')
398             {
399                 while (((c = getc(fd))) == ' ' || (c == '\n'));
400             }
401         }
402
403         if (c == '\r')
404             continue;
405
406         if (c == '#')
407         {
408             /* comment */
409             do
410             {
411                 c = getc(fd);
412
413                 if (c == '\r')
414                     continue;
415                 /* goes to end of line */
416
417                 if (c == '\n')
418                     return 0;
419
420                 if (c == EOF)
421                     return -1;
422             }
423             while (true);
424         }
425
426         s[i = 0] = (char) c;
427
428         while ((c >= '0') && (c <= '9'))
429         {
430             c = getc(fd);
431             s[++i] = (char) c;
432         }
433
434         if (c == '.')
435         {
436             while (((c = getc(fd)) == ' ') || (c == '.') || (c == '\n'));
437             s[i = 0] = (char) c;
438         }
439
440         while (((c = getc(fd)) != '?') && (c != '!') && (c != ' ')
441                && (c != '(') && (c != '\n') && (c != '\t') && (c != EOF))
442         {
443             if (c == '\r')
444                 continue;
445
446             if ((c != 'x') && (c != '-') && (c != ',')
447                 && (c != ';') && (c != '='))
448             {
449                 s[++i] = (char) c;
450             }
451         }
452
453         s[++i] = '\0';
454
455         if (c == '(')
456         {
457             while (((c = getc(fd)) != ')') && (c != EOF));
458
459             if (c == ')')
460                 c = getc(fd);
461         }
462
463         if (c == EOF)
464             return -1;
465
466         if (s[0] == '#')
467         {
468             while ((c != '\n') && (c != EOF))
469                 c = getc(fd);
470
471             if (c == EOF)
472                 return -1;
473             else
474                 return 0;
475         }
476
477         if (strcmp(s, "draw") == 0)
478             continue;
479         else if (strcmp(s, "1-0") == 0)
480             continue;
481         else if (strcmp(s, "0-1") == 0)
482             continue;
483         else if (strcmp(s, "Resigns") == 0)
484             continue;
485         else if (strcmp(s, "Resigns.") == 0)
486             continue;
487         else if (strcmp(s, "Sennichite") == 0)
488             continue;
489         else if (strcmp(s, "Sennichite.") == 0)
490             continue;
491         else if (strcmp(s, "Jishogi") == 0)
492             continue;
493         else if (strcmp(s, "Jishogi.") == 0)
494             continue;
495
496         bhashkey = hashkey;
497         bhashbd  = hashbd;
498
499         i = BVerifyMove(s, mv, moveno);
500
501         if (c == '?')
502         {
503             /* Bad move, not for the program to play */
504             *flags |= BADMOVE;  /* Flag it ! */
505             while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
506         }
507 #ifdef EASY_OPENINGS
508         else if (c == '~')
509         {
510             /* Do not use by computer */
511             *flags |= BADMOVE;  /* Flag it ! */
512
513             while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
514         }
515 #endif
516         else if (c == '!')
517         {
518             /* Good move */
519             *flags |= GOODMOVE; /* Flag it ! */
520
521             while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
522         }
523         else if (c == '\r')
524         {
525             c = getc(fd);
526         }
527
528         if (c == '(' )
529             while (((c = getc(fd)) != ')') && (c != EOF));
530
531         if (!i)
532         {
533             /* flush to start of next */
534             while (((c = getc(fd)) != '#') && (c != EOF));
535
536             if (c == EOF)
537             {
538                 return -1;
539             }
540             else
541             {
542                 ungetc(c, fd);
543                 return i;
544             }
545         }
546
547         return i;
548     }
549 }
550
551
552 static struct gdxadmin ADMIN;
553 struct gdxadmin B;
554 static struct gdxdata DATA;
555
556 /* lts(l) returns most significant 16 bits of l */
557
558 #if SIZEOF_LONG == 8  /* 64-bit long i.e. 8 bytes */
559 #  define lts(x) (USHORT)(((x >> 48) & 0xfffe) | side)
560 #else
561 #  if defined USE_LTSIMP
562 static USHORT ltsimp(long x)
563 {
564     USHORT n;
565     n = (((x >> 16) & 0xfffe));
566     return n;
567 }
568 #    define lts(x) (USHORT)(ltsimp(x) | side)
569 #  else
570 #    define lts(x) (USHORT)(((x >> 16)&0xfffe) | side)
571 #  endif
572 #endif
573
574
575 /* #define HashValue(l) lts(l) */
576 #define HashValue(l) (USHORT)(l & 0xffff)
577
578
579 static int gfd;
580 static ULONG currentoffset;
581
582
583 #define MAXOFFSET(B) ((B.booksize - 1) * sizeof_gdxdata + sizeof_gdxadmin)
584
585 #define HashOffset(hashkey, B) \
586 { \
587   currentoffset = ((ULONG)hashkey % B.booksize) \
588     * sizeof_gdxdata + sizeof_gdxadmin; \
589 }
590
591
592 #define NextOffset(B) \
593 { \
594   currentoffset += sizeof_gdxdata; \
595   if (currentoffset > B.maxoffset) \
596     currentoffset = sizeof_gdxadmin; \
597 }
598
599
600 #define WriteAdmin() \
601 { \
602   lseek(gfd, 0, 0); \
603   write(gfd, (char *)&ADMIN, sizeof_gdxadmin); \
604 }
605
606 #define WriteData() \
607 { \
608   if (mustwrite ) \
609   { \
610     lseek(gfd, currentoffset, 0); \
611     write(gfd, (char *)&DATA, sizeof_gdxdata); \
612     mustwrite = false; \
613   } \
614 }
615
616 static int ReadAdmin(void)
617 {
618     lseek(gfd, 0, 0);
619     return (sizeof_gdxadmin == read(gfd, (char *)&ADMIN, sizeof_gdxadmin));
620 }
621
622 static int ReadData(struct gdxdata *DATA)
623 {
624     lseek(gfd, currentoffset, 0);
625     return (sizeof_gdxdata == read(gfd, (char *)DATA, sizeof_gdxdata));
626 }
627
628
629 /*
630  * GetOpenings()
631  *
632  * CHECKME: is this still valid esp. wrt gnushogi.book?
633  *
634  * Read in the Opening Book file and parse the algebraic notation for a move
635  * into an unsigned integer format indicating the from and to square. Create
636  * a linked list of opening lines of play, with entry->next pointing to the
637  * next line and entry->move pointing to a chunk of memory containing the
638  * moves. More Opening lines of up to 100 half moves may be added to
639  * gnushogi.book. But now it's a hashed table by position which yields a move
640  * or moves for each position. It no longer knows about openings per se only
641  * positions and recommended moves in those positions.
642  *
643  */
644
645 void
646 GetOpenings(void)
647 {
648     short i;
649     int mustwrite = false, first;
650     unsigned short xside, side;
651     short c;
652     USHORT mv, flags;
653     unsigned int x;
654     unsigned int games = 0;
655     LONG collisions = 0;
656     char msg[80];
657
658     FILE *fd;
659
660     if ((fd = fopen(bookfile, "r")) == NULL)
661         fd = fopen("gnushogi.tbk", "r");
662
663     if (fd != NULL)
664     {
665         /* yes add to book */
666         /* open book as writer */
667         gfd = open(binbookfile, O_RDONLY | O_BINARY);
668
669         if (gfd >= 0)
670         {
671             if (ReadAdmin())
672             {
673                 B.bookcount = ADMIN.bookcount;
674                 B.booksize = ADMIN.booksize;
675                 B.maxoffset = ADMIN.maxoffset;
676
677                 if (B.booksize && !(B.maxoffset == MAXOFFSET(B)))
678                 {
679                     printf("bad format %s\n", binbookfile);
680                     exit(1);
681                 }
682             }
683             else
684             {
685                 printf("bad format %s\n", binbookfile);
686                 exit(1);
687             }
688             close(gfd);
689             gfd = open(binbookfile, O_RDWR | O_BINARY);
690
691         }
692         else
693         {
694             gfd = open(binbookfile, O_RDWR | O_CREAT | O_BINARY, 0644);
695
696             ADMIN.bookcount = B.bookcount = 0;
697             ADMIN.booksize = B.booksize = booksize;
698             B.maxoffset = ADMIN.maxoffset = MAXOFFSET(B);
699             DATA.hashbd = 0;
700             DATA.hashkey = 0;
701             DATA.bmove = 0;
702             DATA.flags = 0;
703             DATA.hint = 0;
704             DATA.count = 0;
705             write(gfd, (char *)&ADMIN, sizeof_gdxadmin);
706             printf("creating bookfile %s %ld %ld\n",
707                     binbookfile, B.maxoffset, B.booksize);
708
709             for (x = 0; x < B.booksize; x++)
710             {
711                 write(gfd, (char *)&DATA, sizeof_gdxdata);
712             }
713         }
714
715         if (gfd >= 0)
716         {
717             /* setvbuf(fd, buffr, _IOFBF, 2048); */
718             side = black;
719             xside = white;
720             hashbd = hashkey = 0;
721             i = 0;
722
723             while ((c = Vparse(fd, &mv, &flags, side, i)) >= 0)
724             {
725                 if (c == 1)
726                 {
727                     /*
728                      * If this is not the first move of an opening and
729                      * if it's the first time we have seen it then
730                      * save the next move as a hint.
731                      */
732                     i++;
733
734                     if (i < bookmaxply + 2)
735                     {
736                         if (i > 1 && !(flags & BADMOVE))
737                             DATA.hint = mv;
738
739                         if (i < bookmaxply + 1)
740                         {
741                             /*
742                              * See if this position and move already
743                              * exist from some other opening.
744                              */
745
746                             WriteData();
747                             HashOffset(bhashkey, B);
748                             first = true;
749
750                             while (true)
751                             {
752                                 if (!ReadData(&DATA))
753                                     break; /* corrupted binbook file */
754
755                                 if (DATA.bmove == 0)
756                                     break;  /* free entry */
757
758                                 if (DATA.hashkey == HashValue(bhashkey)
759                                     && DATA.hashbd == bhashbd)
760                                 {
761                                     if (DATA.bmove == mv)
762                                     {
763                                         /*
764                                          * Yes, so just bump count - count
765                                          * is used to choose the opening
766                                          * move in proportion to its
767                                          * presence in the book.
768                                          */
769
770                                         DATA.count++;
771                                         DATA.flags |= flags;
772                                         mustwrite = true;
773                                         break;
774                                     }
775                                     else
776                                     {
777                                         if (first)
778                                             collisions++;
779
780                                         if (DATA.flags & LASTMOVE)
781                                         {
782                                             DATA.flags &= (~LASTMOVE);
783                                             mustwrite = true;
784                                             WriteData();
785                                         }
786                                     }
787                                 }
788
789                                 NextOffset(B);
790                                 first = false;
791                             }
792
793                             /*
794                              * Doesn't exist so add it to the book.
795                              */
796
797                             if (!mustwrite)
798                             {
799                                 B.bookcount++;
800
801                                 if ((B.bookcount % 1000) == 0)
802                                 {
803                                     /* CHECKME: may want to get rid of this,
804                                      * especially for xshogi. */
805                                     printf("%ld rec %d openings "
806                                            "processed\n",
807                                            B.bookcount, games);
808                                 }
809
810                                 /* initialize a record */
811                                 DATA.hashbd = bhashbd;
812                                 DATA.hashkey = HashValue(bhashkey);
813                                 DATA.bmove = mv;
814                                 DATA.flags = flags | LASTMOVE;
815                                 DATA.count = 1;
816                                 DATA.hint = 0;
817                                 mustwrite = true;
818                             }
819                         }
820                     }
821
822                     computer = opponent;
823                     opponent = computer ^ 1;
824
825                     xside = side;
826                     side = side ^ 1;
827                 }
828                 else if (i > 0)
829                 {
830                     /* reset for next opening */
831                     games++;
832                     WriteData();
833                     RESET();
834                     i = 0;
835                     side = black;
836                     xside = white;
837
838                 }
839             }
840
841             WriteData();
842             fclose(fd);
843             /* write admin rec with counts */
844             ADMIN.bookcount = B.bookcount;
845             WriteAdmin();
846
847             close(gfd);
848         }
849     }
850
851     if (binbookfile != NULL)
852     {
853         /* open book as reader */
854         gfd = open(binbookfile, O_RDONLY | O_BINARY);
855
856         if (gfd >= 0)
857         {
858             if (ReadAdmin() && (!ADMIN.booksize
859                                 || (ADMIN.maxoffset == MAXOFFSET(ADMIN))))
860             {
861                 B.bookcount = ADMIN.bookcount;
862                 B.booksize  = ADMIN.booksize;
863                 B.maxoffset = ADMIN.maxoffset;
864             }
865             else
866             {
867                 printf("bad format %s\n", binbookfile);
868                 exit(1);
869             }
870
871         }
872         else
873         {
874             B.bookcount = 0;
875             B.booksize = booksize;
876
877         }
878
879         sprintf(msg, "Book used %d(%d).", B.bookcount, B.booksize);
880         ShowMessage(msg);
881     }
882
883     /* Set everything back to start the game. */
884     Book = BOOKFAIL;
885     RESET();
886
887     /* Now get ready to play .*/
888     if (!B.bookcount)
889     {
890         ShowMessage("Can't find book.");
891         Book = 0;
892     }
893 }
894
895
896
897 /*
898  * OpeningBook(hint, side)
899  *
900  * Go through each of the opening lines of play and check for a match with
901  * the current game listing. If a match occurs, generate a random
902  * number. If this number is the largest generated so far then the next
903  * move in this line becomes the current "candidate".  After all lines are
904  * checked, the candidate move is put at the top of the Tree[] array and
905  * will be played by the program.  Note that the program does not handle
906  * book transpositions.
907  */
908
909 int
910 OpeningBook(unsigned short *hint, short side)
911 {
912     unsigned short r, m;
913     int possibles = TrPnt[2] - TrPnt[1];
914
915     gsrand((unsigned int) time((long *) 0));
916     m = 0;
917
918     /*
919      * Find all the moves for this position  - count them and get their
920      * total count.
921      */
922
923     {
924         USHORT i, x;
925         USHORT rec = 0;
926         USHORT summ = 0;
927         USHORT h = 0, b = 0;
928         struct gdxdata OBB[128];
929
930         if (B.bookcount == 0)
931         {
932             Book--;
933             return false;
934         }
935
936         x = 0;
937         HashOffset(hashkey, B);
938 #ifdef BOOKTEST
939         printf("looking for book move, bhashbd = 0x%lx bhashkey = 0x%x\n",
940                (ULONG)hashbd, HashValue(hashkey));
941 #endif
942         while (true)
943         {
944             if (!ReadData(&OBB[x]))
945                 break;
946
947             if (OBB[x].bmove == 0)
948                 break;
949
950 #ifdef BOOKTEST
951             printf("compare with bhashbd = 0x%lx bhashkey = 0x%x\n",
952                    OBB[x].hashbd, OBB[x].hashkey);
953 #endif
954             if ((OBB[x].hashkey == HashValue(hashkey))
955                 && (OBB[x].hashbd == (ULONG)hashbd))
956             {
957                 x++;
958
959                 if (OBB[x-1].flags & LASTMOVE)
960                     break;
961             }
962
963             NextOffset(B);
964         }
965
966 #ifdef BOOKTEST
967         printf("%d book move(s) found.\n", x);
968 #endif
969
970         if (x == 0)
971         {
972             Book--;
973             return false;
974         }
975
976         for (i = 0; i < x; i++)
977         {
978             if (OBB[i].flags & BADMOVE)
979             {
980                 m = OBB[i].bmove;
981
982                 /* Is the move in the MoveList? */
983                 for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
984                 {
985                     if (((Tree[b].f << 8) | Tree[b].t) == m)
986                     {
987                         if (--possibles)
988                             Tree[b].score = DONTUSE;
989                         break;
990                     }
991                 }
992             }
993             else
994             {
995 #if defined BOOKTEST
996                 char s[20];
997                 movealgbr(m = OBB[i].bmove, s);
998                 printf("finding book move: %s\n", s);
999 #endif
1000                 summ += OBB[i].count;
1001             }
1002         }
1003
1004         if (summ == 0)
1005         {
1006             Book--;
1007             return false;
1008         }
1009
1010         r = (urand() % summ);
1011
1012         for (i = 0; i < x; i++)
1013         {
1014             if (!(OBB[i].flags & BADMOVE))
1015             {
1016                 if (r < OBB[i].count)
1017                 {
1018                     rec = i;
1019                     break;
1020                 }
1021                 else
1022                 {
1023                     r -= OBB[i].count;
1024                 }
1025             }
1026         }
1027
1028         h = OBB[rec].hint;
1029         m = OBB[rec].bmove;
1030
1031         /* Make sure the move is in the MoveList. */
1032         for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
1033         {
1034             if (((Tree[b].f << 8) | Tree[b].t) == m)
1035             {
1036                 Tree[b].flags |= book;
1037                 Tree[b].score = 0;
1038                 break;
1039             }
1040         }
1041
1042         /* Make sure it's the best. */
1043
1044         pick(TrPnt[1], TrPnt[2] - 1);
1045
1046         if (Tree[TrPnt[1]].score)
1047         {
1048             /* no! */
1049             Book--;
1050             return false;
1051         }
1052
1053         /* Ok, pick up the hint and go. */
1054         *hint = h;
1055         return true;
1056     }
1057
1058     Book--;
1059     return false;
1060 }
1061
1062
1063