Deal with simple compiler warnings: dead code, unused parameters, etc.
[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, 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 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             hashbd = hashkey = 0;
719             i = 0;
720
721             while ((c = Vparse(fd, &mv, &flags, i)) >= 0)
722             {
723                 if (c == 1)
724                 {
725                     /*
726                      * If this is not the first move of an opening and
727                      * if it's the first time we have seen it then
728                      * save the next move as a hint.
729                      */
730                     i++;
731
732                     if (i < bookmaxply + 2)
733                     {
734                         if (i > 1 && !(flags & BADMOVE))
735                             DATA.hint = mv;
736
737                         if (i < bookmaxply + 1)
738                         {
739                             /*
740                              * See if this position and move already
741                              * exist from some other opening.
742                              */
743
744                             WriteData();
745                             HashOffset(bhashkey, B);
746                             first = true;
747
748                             while (true)
749                             {
750                                 if (!ReadData(&DATA))
751                                     break; /* corrupted binbook file */
752
753                                 if (DATA.bmove == 0)
754                                     break;  /* free entry */
755
756                                 if (DATA.hashkey == HashValue(bhashkey)
757                                     && DATA.hashbd == bhashbd)
758                                 {
759                                     if (DATA.bmove == mv)
760                                     {
761                                         /*
762                                          * Yes, so just bump count - count
763                                          * is used to choose the opening
764                                          * move in proportion to its
765                                          * presence in the book.
766                                          */
767
768                                         DATA.count++;
769                                         DATA.flags |= flags;
770                                         mustwrite = true;
771                                         break;
772                                     }
773                                     else
774                                     {
775                                         if (first)
776                                             collisions++;
777
778                                         if (DATA.flags & LASTMOVE)
779                                         {
780                                             DATA.flags &= (~LASTMOVE);
781                                             mustwrite = true;
782                                             WriteData();
783                                         }
784                                     }
785                                 }
786
787                                 NextOffset(B);
788                                 first = false;
789                             }
790
791                             /*
792                              * Doesn't exist so add it to the book.
793                              */
794
795                             if (!mustwrite)
796                             {
797                                 B.bookcount++;
798
799                                 if ((B.bookcount % 1000) == 0)
800                                 {
801                                     /* CHECKME: may want to get rid of this,
802                                      * especially for xshogi. */
803                                     printf("%ld rec %d openings "
804                                            "processed\n",
805                                            B.bookcount, games);
806                                 }
807
808                                 /* initialize a record */
809                                 DATA.hashbd = bhashbd;
810                                 DATA.hashkey = HashValue(bhashkey);
811                                 DATA.bmove = mv;
812                                 DATA.flags = flags | LASTMOVE;
813                                 DATA.count = 1;
814                                 DATA.hint = 0;
815                                 mustwrite = true;
816                             }
817                         }
818                     }
819
820                     computer = opponent;
821                     opponent = computer ^ 1;
822
823                     side = side ^ 1;
824                 }
825                 else if (i > 0)
826                 {
827                     /* reset for next opening */
828                     games++;
829                     WriteData();
830                     RESET();
831                     i = 0;
832                     side = black;
833                 }
834             }
835
836             WriteData();
837             fclose(fd);
838             /* write admin rec with counts */
839             ADMIN.bookcount = B.bookcount;
840             WriteAdmin();
841
842             close(gfd);
843         }
844     }
845
846     if (binbookfile != NULL)
847     {
848         /* open book as reader */
849         gfd = open(binbookfile, O_RDONLY | O_BINARY);
850
851         if (gfd >= 0)
852         {
853             if (ReadAdmin() && (!ADMIN.booksize
854                                 || (ADMIN.maxoffset == MAXOFFSET(ADMIN))))
855             {
856                 B.bookcount = ADMIN.bookcount;
857                 B.booksize  = ADMIN.booksize;
858                 B.maxoffset = ADMIN.maxoffset;
859             }
860             else
861             {
862                 printf("bad format %s\n", binbookfile);
863                 exit(1);
864             }
865
866         }
867         else
868         {
869             B.bookcount = 0;
870             B.booksize = booksize;
871
872         }
873
874         sprintf(msg, "Book used %lu(%lu).", B.bookcount, B.booksize);
875         dsp->ShowMessage(msg);
876     }
877
878     /* Set everything back to start the game. */
879     Book = BOOKFAIL;
880     RESET();
881
882     /* Now get ready to play .*/
883     if (!B.bookcount)
884     {
885         dsp->ShowMessage("Can't find book.");
886         Book = 0;
887     }
888 }
889
890
891
892 /*
893  * OpeningBook(hint)
894  *
895  * Go through each of the opening lines of play and check for a match with
896  * the current game listing. If a match occurs, generate a random
897  * number. If this number is the largest generated so far then the next
898  * move in this line becomes the current "candidate".  After all lines are
899  * checked, the candidate move is put at the top of the Tree[] array and
900  * will be played by the program.  Note that the program does not handle
901  * book transpositions.
902  */
903
904 int
905 OpeningBook(unsigned short *hint)
906 {
907     unsigned short r, m;
908     int possibles = TrPnt[2] - TrPnt[1];
909
910     gsrand((unsigned int) time((long *) 0));
911     m = 0;
912
913     /*
914      * Find all the moves for this position  - count them and get their
915      * total count.
916      */
917
918     {
919         USHORT i, x;
920         USHORT rec = 0;
921         USHORT summ = 0;
922         USHORT h = 0, b = 0;
923         struct gdxdata OBB[128];
924
925         if (B.bookcount == 0)
926         {
927             Book--;
928             return false;
929         }
930
931         x = 0;
932         HashOffset(hashkey, B);
933 #ifdef BOOKTEST
934         printf("looking for book move, bhashbd = 0x%lx bhashkey = 0x%x\n",
935                (ULONG)hashbd, HashValue(hashkey));
936 #endif
937         while (true)
938         {
939             if (!ReadData(&OBB[x]))
940                 break;
941
942             if (OBB[x].bmove == 0)
943                 break;
944
945 #ifdef BOOKTEST
946             printf("compare with bhashbd = 0x%lx bhashkey = 0x%x\n",
947                    OBB[x].hashbd, OBB[x].hashkey);
948 #endif
949             if ((OBB[x].hashkey == HashValue(hashkey))
950                 && (OBB[x].hashbd == (ULONG)hashbd))
951             {
952                 x++;
953
954                 if (OBB[x-1].flags & LASTMOVE)
955                     break;
956             }
957
958             NextOffset(B);
959         }
960
961 #ifdef BOOKTEST
962         printf("%d book move(s) found.\n", x);
963 #endif
964
965         if (x == 0)
966         {
967             Book--;
968             return false;
969         }
970
971         for (i = 0; i < x; i++)
972         {
973             if (OBB[i].flags & BADMOVE)
974             {
975                 m = OBB[i].bmove;
976
977                 /* Is the move in the MoveList? */
978                 for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
979                 {
980                     if (((Tree[b].f << 8) | Tree[b].t) == m)
981                     {
982                         if (--possibles)
983                             Tree[b].score = DONTUSE;
984                         break;
985                     }
986                 }
987             }
988             else
989             {
990 #if defined BOOKTEST
991                 char s[20];
992                 movealgbr(m = OBB[i].bmove, s);
993                 printf("finding book move: %s\n", s);
994 #endif
995                 summ += OBB[i].count;
996             }
997         }
998
999         if (summ == 0)
1000         {
1001             Book--;
1002             return false;
1003         }
1004
1005         r = (urand() % summ);
1006
1007         for (i = 0; i < x; i++)
1008         {
1009             if (!(OBB[i].flags & BADMOVE))
1010             {
1011                 if (r < OBB[i].count)
1012                 {
1013                     rec = i;
1014                     break;
1015                 }
1016                 else
1017                 {
1018                     r -= OBB[i].count;
1019                 }
1020             }
1021         }
1022
1023         h = OBB[rec].hint;
1024         m = OBB[rec].bmove;
1025
1026         /* Make sure the move is in the MoveList. */
1027         for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
1028         {
1029             if (((Tree[b].f << 8) | Tree[b].t) == m)
1030             {
1031                 Tree[b].flags |= book;
1032                 Tree[b].score = 0;
1033                 break;
1034             }
1035         }
1036
1037         /* Make sure it's the best. */
1038
1039         pick(TrPnt[1], TrPnt[2] - 1);
1040
1041         if (Tree[TrPnt[1]].score)
1042         {
1043             /* no! */
1044             Book--;
1045             return false;
1046         }
1047
1048         /* Ok, pick up the hint and go. */
1049         *hint = h;
1050         return true;
1051     }
1052
1053     Book--;
1054     return false;
1055 }
1056
1057
1058