Changing license to GPL3 (and bumping version to 1.4.0).
[gnushogi.git] / gnushogi / book.c
1 /*
2  * FILE: book.c
3  *
4  * ----------------------------------------------------------------------
5  *
6  * Copyright (c) 2012 Free Software Foundation
7  *
8  * GNU SHOGI is based on GNU CHESS
9  *
10  * This file is part of GNU SHOGI.
11  *
12  * GNU Shogi is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU General Public License as published by the
14  * Free Software Foundation; either version 3 of the License,
15  * or (at your option) any later version.
16  *
17  * GNU Shogi is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20  * for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with GNU Shogi; see the file COPYING. If not, see
24  * <http://www.gnu.org/licenses/>.
25  * ----------------------------------------------------------------------
26  *
27  */
28
29 #include "gnushogi.h"
30
31 #define O_BINARY 0
32
33 #if HAVE_UNISTD_H
34 /* Declarations of read(), write(), close(), and lseek(). */
35 #include <unistd.h>
36 #endif
37
38 #if HAVE_FCNTL_H
39 #include <fcntl.h>
40 #endif
41
42 #include "book.h"
43
44 unsigned booksize = BOOKSIZE;
45 unsigned short bookmaxply = BOOKMAXPLY;
46 unsigned bookcount = 0;
47
48 #ifdef BOOK
49 char *bookfile = BOOK;
50 #else
51 char *bookfile = NULL;
52 #endif
53
54 #ifdef BINBOOK
55 char *binbookfile = BINBOOK;
56 #else
57 char *binbookfile = NULL;
58 #endif
59
60 static char bmvstr[3][7];
61
62 static ULONG bhashbd;
63 static ULONG bhashkey;
64
65
66 /*
67  * Balgbr(f, t, flag)
68  *
69  * Generate move strings in different formats.
70  */
71
72 void
73 Balgbr(short f, short t, short flag)
74 {
75     short promoted = false;
76
77     if ((f & 0x80) != 0)
78     {
79         f &= 0x7f;
80         promoted = true;
81     }
82
83     if (f > NO_SQUARES)
84     {
85         short piece;
86         piece = f - NO_SQUARES;
87
88         if (f > (NO_SQUARES + NO_PIECES))
89             piece -= NO_PIECES;
90
91         flag = (dropmask | piece);
92     }
93
94     if ((t & 0x80) != 0)
95     {
96         flag |= promote;
97         t &= 0x7f;
98     }
99
100     if ((f == t) && ((f != 0) || (t != 0)))
101     {
102         /*
103          * error in algbr: FROM=TO=t
104          */
105
106         bmvstr[0][0] = bmvstr[1][0] = bmvstr[2][0] = '\0';
107     }
108     else
109     {
110         if ((flag & dropmask) != 0)
111         {
112             /* bmvstr[0]: P*3c bmvstr[1]: P'3c */
113             short piece = flag & pmask;
114             bmvstr[0][0] = pxx[piece];
115             bmvstr[0][1] = '*';
116             bmvstr[0][2] = cxx[column(t)];
117             bmvstr[0][3] = rxx[row(t)];
118             bmvstr[0][4] = bmvstr[2][0] = '\0';
119             strcpy(bmvstr[1], bmvstr[0]);
120             bmvstr[1][1] = '\'';
121         }
122         else
123         {
124             if ((f != 0) || (t != 0))
125             {
126                 /* algebraic notation */
127                 /* bmvstr[0]: 7g7f bmvstr[1]:
128                  * (+)P7g7f(+) bmvstr[2]: (+)P7f(+) */
129                 bmvstr[0][0] = cxx[column(f)];
130                 bmvstr[0][1] = rxx[row(f)];
131                 bmvstr[0][2] = cxx[column(t)];
132                 bmvstr[0][3] = rxx[row(t)];
133                 bmvstr[0][4] = '\0';
134
135                 if (promoted)
136                 {
137                     bmvstr[1][0] = bmvstr[2][0] = '+';
138                     bmvstr[1][1] = bmvstr[2][1] = pxx[board[f]];
139                     strcpy(&bmvstr[1][2], &bmvstr[0][0]);
140                     strcpy(&bmvstr[2][2], &bmvstr[0][2]);
141                 }
142                 else
143                 {
144                     bmvstr[1][0] = bmvstr[2][0] = pxx[board[f]];
145                     strcpy(&bmvstr[1][1], &bmvstr[0][0]);
146                     strcpy(&bmvstr[2][1], &bmvstr[0][2]);
147                 }
148
149                 if (flag & promote)
150                 {
151                     strcat(bmvstr[0], "+");
152                     strcat(bmvstr[1], "+");
153                     strcat(bmvstr[2], "+");
154                 }
155             }
156             else
157             {
158                 bmvstr[0][0] = bmvstr[1][0] = bmvstr[2][0] = '\0';
159             }
160         }
161     }
162 }
163
164
165
166
167 #ifndef QUIETBOOKGEN
168 void
169 bkdisplay(char *s, int cnt, int moveno)
170 {
171     static short pnt;
172     struct leaf  *node;
173     int r, c, l;
174
175     pnt = TrPnt[2];
176     printf("matches = %d\n", cnt);
177     printf("inout move is :%s: move number %d side %s\n",
178             s, moveno / 2 + 1, (moveno & 1) ? "white" : "black");
179
180 #ifndef SEMIQUIETBOOKGEN
181     printf("legal moves are \n");
182
183     while (pnt < TrPnt[3])
184     {
185         node = &Tree[pnt++];
186
187         if (is_promoted[board[node->f]] )
188             Balgbr(node->f | 0x80, node->t, (short) node->flags);
189         else
190             Balgbr(node->f, node->t, (short) node->flags);
191
192         printf("%s %s %s\n",
193                bmvstr[0], bmvstr[1], bmvstr[2]);
194     }
195
196     printf("\n current board is\n");
197
198     for (r = (NO_ROWS - 1); r >= 0; r--)
199     {
200         for (c = 0; c <= (NO_COLS - 1); c++)
201         {
202             char pc;
203
204             l = locn(r, c);
205             pc = (is_promoted[board[l]] ? '+' : ' ');
206
207             if (color[l] == neutral)
208                 printf(" -");
209             else if (color[l] == black)
210                 printf("%c%c", pc, qxx[board[l]]);
211             else
212                 printf("%c%c", pc, pxx[board[l]]);
213         }
214
215         printf("\n");
216     }
217
218     printf("\n");
219     {
220         short color;
221
222         for (color = black; color <= white; color++)
223         {
224             short piece, c;
225
226             printf((color == black) ? "black " : "white ");
227
228             for (piece = pawn; piece <= king; piece++)
229             {
230                 if ((c = Captured[color][piece]))
231                     printf("%i%c ", c, pxx[piece]);
232             }
233
234             printf("\n");
235         };
236     }
237 #endif /* SEMIQUIETBOOKGEN */
238 }
239
240 #endif /* QUIETBOOKGEN */
241
242
243
244 /*
245  * BVerifyMove(s, mv, moveno)
246  *
247  * Compare the string 's' to the list of legal moves available for the
248  * opponent. If a match is found, make the move on the board.
249  */
250
251 int
252 BVerifyMove(char *s, unsigned short *mv, int moveno)
253 {
254     static short pnt, tempb, tempc, tempsf, tempst, cnt;
255     static struct leaf xnode;
256     struct leaf  *node;
257
258     *mv = 0;
259     cnt = 0;
260     MoveList(opponent, 2, -2, true);
261     pnt = TrPnt[2];
262
263     while (pnt < TrPnt[3])
264     {
265         node = &Tree[pnt++];
266
267         if (is_promoted[board[node->f]] )
268             Balgbr(node->f | 0x80, node->t, (short) node->flags);
269         else
270             Balgbr(node->f, node->t, (short) node->flags);
271
272         if (strcmp(s, bmvstr[0]) == 0 || strcmp(s, bmvstr[1]) == 0 ||
273             strcmp(s, bmvstr[2]) == 0)
274         {
275             cnt++;
276             xnode = *node;
277         }
278     }
279
280     if (cnt == 1)
281     {
282         short blockable;
283
284         MakeMove(opponent, &xnode, &tempb,
285                  &tempc, &tempsf, &tempst, &INCscore);
286
287         if (SqAttacked(PieceList[opponent][0], computer, &blockable))
288         {
289             UnmakeMove(opponent, &xnode, &tempb, &tempc, &tempsf, &tempst);
290             /* Illegal move in check */
291 #if !defined QUIETBOOKGEN
292             /* 077: "Illegal move (in check) %s" */
293             printf(CP[77]);
294             printf("\n");
295             bkdisplay(s, cnt, moveno);
296 #endif
297             return false;
298         }
299         else
300         {
301             *mv = (xnode.f << 8) | xnode.t;
302
303             if (is_promoted[board[xnode.t]] )
304                 Balgbr(xnode.f | 0x80, xnode.t, false);
305             else
306                 Balgbr(xnode.f, xnode.t, false);
307
308             return true;
309         }
310     }
311
312     /* Illegal move */
313 #if !defined QUIETBOOKGEN
314     /* 075: "Illegal move (no match)%s\n" */
315     printf(CP[75], 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         /* 213: "Book used %d(%d)." */
879         sprintf(msg, CP[213], 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         /* 212: "Can't find book." */
891         ShowMessage(CP[212]);
892         Book = 0;
893     }
894 }
895
896
897
898 /*
899  * OpeningBook(hint, side)
900  *
901  * Go through each of the opening lines of play and check for a match with
902  * the current game listing. If a match occurs, generate a random
903  * number. If this number is the largest generated so far then the next
904  * move in this line becomes the current "candidate".  After all lines are
905  * checked, the candidate move is put at the top of the Tree[] array and
906  * will be played by the program.  Note that the program does not handle
907  * book transpositions.
908  */
909
910 int
911 OpeningBook(unsigned short *hint, short side)
912 {
913     unsigned short r, m;
914     int possibles = TrPnt[2] - TrPnt[1];
915
916     gsrand((unsigned int) time((long *) 0));
917     m = 0;
918
919     /*
920      * Find all the moves for this position  - count them and get their
921      * total count.
922      */
923
924     {
925         USHORT i, x;
926         USHORT rec = 0;
927         USHORT summ = 0;
928         USHORT h = 0, b = 0;
929         struct gdxdata OBB[128];
930
931         if (B.bookcount == 0)
932         {
933             Book--;
934             return false;
935         }
936
937         x = 0;
938         HashOffset(hashkey, B);
939 #ifdef BOOKTEST
940         printf("looking for book move, bhashbd = 0x%lx bhashkey = 0x%x\n",
941                (ULONG)hashbd, HashValue(hashkey));
942 #endif
943         while (true)
944         {
945             if (!ReadData(&OBB[x]))
946                 break;
947
948             if (OBB[x].bmove == 0)
949                 break;
950
951 #ifdef BOOKTEST
952             printf("compare with bhashbd = 0x%lx bhashkey = 0x%x\n",
953                    OBB[x].hashbd, OBB[x].hashkey);
954 #endif
955             if ((OBB[x].hashkey == HashValue(hashkey))
956                 && (OBB[x].hashbd == (ULONG)hashbd))
957             {
958                 x++;
959
960                 if (OBB[x-1].flags & LASTMOVE)
961                     break;
962             }
963
964             NextOffset(B);
965         }
966
967 #ifdef BOOKTEST
968         printf("%d book move(s) found.\n", x);
969 #endif
970
971         if (x == 0)
972         {
973             Book--;
974             return false;
975         }
976
977         for (i = 0; i < x; i++)
978         {
979             if (OBB[i].flags & BADMOVE)
980             {
981                 m = OBB[i].bmove;
982
983                 /* Is the move in the MoveList? */
984                 for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
985                 {
986                     if (((Tree[b].f << 8) | Tree[b].t) == m)
987                     {
988                         if (--possibles)
989                             Tree[b].score = DONTUSE;
990                         break;
991                     }
992                 }
993             }
994             else
995             {
996 #if defined BOOKTEST
997                 char s[20];
998                 movealgbr(m = OBB[i].bmove, s);
999                 printf("finding book move: %s\n", s);
1000 #endif
1001                 summ += OBB[i].count;
1002             }
1003         }
1004
1005         if (summ == 0)
1006         {
1007             Book--;
1008             return false;
1009         }
1010
1011         r = (urand() % summ);
1012
1013         for (i = 0; i < x; i++)
1014         {
1015             if (!(OBB[i].flags & BADMOVE))
1016             {
1017                 if (r < OBB[i].count)
1018                 {
1019                     rec = i;
1020                     break;
1021                 }
1022                 else
1023                 {
1024                     r -= OBB[i].count;
1025                 }
1026             }
1027         }
1028
1029         h = OBB[rec].hint;
1030         m = OBB[rec].bmove;
1031
1032         /* Make sure the move is in the MoveList. */
1033         for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
1034         {
1035             if (((Tree[b].f << 8) | Tree[b].t) == m)
1036             {
1037                 Tree[b].flags |= book;
1038                 Tree[b].score = 0;
1039                 break;
1040             }
1041         }
1042
1043         /* Make sure it's the best. */
1044
1045         pick(TrPnt[1], TrPnt[2] - 1);
1046
1047         if (Tree[TrPnt[1]].score)
1048         {
1049             /* no! */
1050             Book--;
1051             return false;
1052         }
1053
1054         /* Ok, pick up the hint and go. */
1055         *hint = h;
1056         return true;
1057     }
1058
1059     Book--;
1060     return false;
1061 }
1062
1063
1064