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