Deal with warnings raised by -Wformat-security.
[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             puts(CP[77]);
294             bkdisplay(s, cnt, moveno);
295 #endif
296             return false;
297         }
298         else
299         {
300             *mv = (xnode.f << 8) | xnode.t;
301
302             if (is_promoted[board[xnode.t]] )
303                 Balgbr(xnode.f | 0x80, xnode.t, false);
304             else
305                 Balgbr(xnode.f, xnode.t, false);
306
307             return true;
308         }
309     }
310
311     /* Illegal move */
312 #if !defined QUIETBOOKGEN
313     /* 075: "Illegal move (no match)%s\n" */
314     printf(CP[75], s);
315     bkdisplay(s, cnt, moveno);
316 #endif
317     return false;
318 }
319
320
321
322
323 /*
324  * RESET()
325  *
326  * Reset the board and other variables to start a new game.
327  *
328  */
329
330 void
331 RESET(void)
332 {
333     short l;
334
335     flag.illegal = flag.mate = flag.post = flag.quit
336         = flag.reverse = flag.bothsides = flag.onemove = flag.force
337         = false;
338
339     flag.material = flag.coords = flag.hash = flag.easy
340         = flag.beep = flag.rcptr
341         = true;
342
343     flag.stars = flag.shade = flag.back = flag.musttimeout = false;
344     flag.gamein = false;
345     GenCnt   = 0;
346     GameCnt  = 0;
347     CptrFlag[0] = TesujiFlag[0] = false;
348     opponent = black;
349     computer = white;
350
351     for (l = 0; l < NO_SQUARES; l++)
352     {
353         board[l]   = Stboard[l];
354         color[l]   = Stcolor[l];
355         Mvboard[l] = 0;
356     }
357
358     ClearCaptured();
359     InitializeStats();
360     hashbd = hashkey = 0;
361 }
362
363
364
365 static
366 int
367 Vparse (FILE * fd, USHORT *mv, USHORT *flags, USHORT side, int moveno)
368 {
369     int c, i;
370     char s[255];
371
372     *flags = 0;
373
374     while (true)
375     {
376         while (((c = getc(fd)) == ' ')
377                || (c == '!') || (c == '/') || (c == '\n'));
378
379         if (c == '(')
380         {
381             /* amount of time spent for the last move */
382             while (((c = getc(fd)) != ')') && (c != EOF));
383
384             if (c == ')')
385             {
386                 while (((c = getc(fd)) == ' ') || (c == '\n'));
387             }
388         }
389
390         if (c == '[')
391         {
392             /* comment for the actual game */
393             while (((c = getc(fd))) != ']' && (c != EOF));
394
395             if (c == ']')
396             {
397                 while (((c = getc(fd))) == ' ' || (c == '\n'));
398             }
399         }
400
401         if (c == '\r')
402             continue;
403
404         if (c == '#')
405         {
406             /* comment */
407             do
408             {
409                 c = getc(fd);
410
411                 if (c == '\r')
412                     continue;
413                 /* goes to end of line */
414
415                 if (c == '\n')
416                     return 0;
417
418                 if (c == EOF)
419                     return -1;
420             }
421             while (true);
422         }
423
424         s[i = 0] = (char) c;
425
426         while ((c >= '0') && (c <= '9'))
427         {
428             c = getc(fd);
429             s[++i] = (char) c;
430         }
431
432         if (c == '.')
433         {
434             while (((c = getc(fd)) == ' ') || (c == '.') || (c == '\n'));
435             s[i = 0] = (char) c;
436         }
437
438         while (((c = getc(fd)) != '?') && (c != '!') && (c != ' ')
439                && (c != '(') && (c != '\n') && (c != '\t') && (c != EOF))
440         {
441             if (c == '\r')
442                 continue;
443
444             if ((c != 'x') && (c != '-') && (c != ',')
445                 && (c != ';') && (c != '='))
446             {
447                 s[++i] = (char) c;
448             }
449         }
450
451         s[++i] = '\0';
452
453         if (c == '(')
454         {
455             while (((c = getc(fd)) != ')') && (c != EOF));
456
457             if (c == ')')
458                 c = getc(fd);
459         }
460
461         if (c == EOF)
462             return -1;
463
464         if (s[0] == '#')
465         {
466             while ((c != '\n') && (c != EOF))
467                 c = getc(fd);
468
469             if (c == EOF)
470                 return -1;
471             else
472                 return 0;
473         }
474
475         if (strcmp(s, "draw") == 0)
476             continue;
477         else if (strcmp(s, "1-0") == 0)
478             continue;
479         else if (strcmp(s, "0-1") == 0)
480             continue;
481         else if (strcmp(s, "Resigns") == 0)
482             continue;
483         else if (strcmp(s, "Resigns.") == 0)
484             continue;
485         else if (strcmp(s, "Sennichite") == 0)
486             continue;
487         else if (strcmp(s, "Sennichite.") == 0)
488             continue;
489         else if (strcmp(s, "Jishogi") == 0)
490             continue;
491         else if (strcmp(s, "Jishogi.") == 0)
492             continue;
493
494         bhashkey = hashkey;
495         bhashbd  = hashbd;
496
497         i = BVerifyMove(s, mv, moveno);
498
499         if (c == '?')
500         {
501             /* Bad move, not for the program to play */
502             *flags |= BADMOVE;  /* Flag it ! */
503             while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
504         }
505 #ifdef EASY_OPENINGS
506         else if (c == '~')
507         {
508             /* Do not use by computer */
509             *flags |= BADMOVE;  /* Flag it ! */
510
511             while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
512         }
513 #endif
514         else if (c == '!')
515         {
516             /* Good move */
517             *flags |= GOODMOVE; /* Flag it ! */
518
519             while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
520         }
521         else if (c == '\r')
522         {
523             c = getc(fd);
524         }
525
526         if (c == '(' )
527             while (((c = getc(fd)) != ')') && (c != EOF));
528
529         if (!i)
530         {
531             /* flush to start of next */
532             while (((c = getc(fd)) != '#') && (c != EOF));
533
534             if (c == EOF)
535             {
536                 return -1;
537             }
538             else
539             {
540                 ungetc(c, fd);
541                 return i;
542             }
543         }
544
545         return i;
546     }
547 }
548
549
550 static struct gdxadmin ADMIN;
551 struct gdxadmin B;
552 static struct gdxdata DATA;
553
554 /* lts(l) returns most significant 16 bits of l */
555
556 #if SIZEOF_LONG == 8  /* 64-bit long i.e. 8 bytes */
557 #  define lts(x) (USHORT)(((x >> 48) & 0xfffe) | side)
558 #else
559 #  if defined USE_LTSIMP
560 static USHORT ltsimp(long x)
561 {
562     USHORT n;
563     n = (((x >> 16) & 0xfffe));
564     return n;
565 }
566 #    define lts(x) (USHORT)(ltsimp(x) | side)
567 #  else
568 #    define lts(x) (USHORT)(((x >> 16)&0xfffe) | side)
569 #  endif
570 #endif
571
572
573 /* #define HashValue(l) lts(l) */
574 #define HashValue(l) (USHORT)(l & 0xffff)
575
576
577 static int gfd;
578 static ULONG currentoffset;
579
580
581 #define MAXOFFSET(B) ((B.booksize - 1) * sizeof_gdxdata + sizeof_gdxadmin)
582
583 #define HashOffset(hashkey, B) \
584 { \
585   currentoffset = ((ULONG)hashkey % B.booksize) \
586     * sizeof_gdxdata + sizeof_gdxadmin; \
587 }
588
589
590 #define NextOffset(B) \
591 { \
592   currentoffset += sizeof_gdxdata; \
593   if (currentoffset > B.maxoffset) \
594     currentoffset = sizeof_gdxadmin; \
595 }
596
597
598 #define WriteAdmin() \
599 { \
600   lseek(gfd, 0, 0); \
601   write(gfd, (char *)&ADMIN, sizeof_gdxadmin); \
602 }
603
604 #define WriteData() \
605 { \
606   if (mustwrite ) \
607   { \
608     lseek(gfd, currentoffset, 0); \
609     write(gfd, (char *)&DATA, sizeof_gdxdata); \
610     mustwrite = false; \
611   } \
612 }
613
614 static int ReadAdmin(void)
615 {
616     lseek(gfd, 0, 0);
617     return (sizeof_gdxadmin == read(gfd, (char *)&ADMIN, sizeof_gdxadmin));
618 }
619
620 static int ReadData(struct gdxdata *DATA)
621 {
622     lseek(gfd, currentoffset, 0);
623     return (sizeof_gdxdata == read(gfd, (char *)DATA, sizeof_gdxdata));
624 }
625
626
627 /*
628  * GetOpenings()
629  *
630  * CHECKME: is this still valid esp. wrt gnushogi.book?
631  *
632  * Read in the Opening Book file and parse the algebraic notation for a move
633  * into an unsigned integer format indicating the from and to square. Create
634  * a linked list of opening lines of play, with entry->next pointing to the
635  * next line and entry->move pointing to a chunk of memory containing the
636  * moves. More Opening lines of up to 100 half moves may be added to
637  * gnushogi.book. But now it's a hashed table by position which yields a move
638  * or moves for each position. It no longer knows about openings per se only
639  * positions and recommended moves in those positions.
640  *
641  */
642
643 void
644 GetOpenings(void)
645 {
646     short i;
647     int mustwrite = false, first;
648     unsigned short xside, side;
649     short c;
650     USHORT mv, flags;
651     unsigned int x;
652     unsigned int games = 0;
653     LONG collisions = 0;
654     char msg[80];
655
656     FILE *fd;
657
658     if ((fd = fopen(bookfile, "r")) == NULL)
659         fd = fopen("gnushogi.tbk", "r");
660
661     if (fd != NULL)
662     {
663         /* yes add to book */
664         /* open book as writer */
665         gfd = open(binbookfile, O_RDONLY | O_BINARY);
666
667         if (gfd >= 0)
668         {
669             if (ReadAdmin())
670             {
671                 B.bookcount = ADMIN.bookcount;
672                 B.booksize = ADMIN.booksize;
673                 B.maxoffset = ADMIN.maxoffset;
674
675                 if (B.booksize && !(B.maxoffset == MAXOFFSET(B)))
676                 {
677                     printf("bad format %s\n", binbookfile);
678                     exit(1);
679                 }
680             }
681             else
682             {
683                 printf("bad format %s\n", binbookfile);
684                 exit(1);
685             }
686             close(gfd);
687             gfd = open(binbookfile, O_RDWR | O_BINARY);
688
689         }
690         else
691         {
692             gfd = open(binbookfile, O_RDWR | O_CREAT | O_BINARY, 0644);
693
694             ADMIN.bookcount = B.bookcount = 0;
695             ADMIN.booksize = B.booksize = booksize;
696             B.maxoffset = ADMIN.maxoffset = MAXOFFSET(B);
697             DATA.hashbd = 0;
698             DATA.hashkey = 0;
699             DATA.bmove = 0;
700             DATA.flags = 0;
701             DATA.hint = 0;
702             DATA.count = 0;
703             write(gfd, (char *)&ADMIN, sizeof_gdxadmin);
704             printf("creating bookfile %s %ld %ld\n",
705                     binbookfile, B.maxoffset, B.booksize);
706
707             for (x = 0; x < B.booksize; x++)
708             {
709                 write(gfd, (char *)&DATA, sizeof_gdxdata);
710             }
711         }
712
713         if (gfd >= 0)
714         {
715             /* setvbuf(fd, buffr, _IOFBF, 2048); */
716             side = black;
717             xside = white;
718             hashbd = hashkey = 0;
719             i = 0;
720
721             while ((c = Vparse(fd, &mv, &flags, side, 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                     xside = side;
824                     side = side ^ 1;
825                 }
826                 else if (i > 0)
827                 {
828                     /* reset for next opening */
829                     games++;
830                     WriteData();
831                     RESET();
832                     i = 0;
833                     side = black;
834                     xside = white;
835
836                 }
837             }
838
839             WriteData();
840             fclose(fd);
841             /* write admin rec with counts */
842             ADMIN.bookcount = B.bookcount;
843             WriteAdmin();
844
845             close(gfd);
846         }
847     }
848
849     if (binbookfile != NULL)
850     {
851         /* open book as reader */
852         gfd = open(binbookfile, O_RDONLY | O_BINARY);
853
854         if (gfd >= 0)
855         {
856             if (ReadAdmin() && (!ADMIN.booksize
857                                 || (ADMIN.maxoffset == MAXOFFSET(ADMIN))))
858             {
859                 B.bookcount = ADMIN.bookcount;
860                 B.booksize  = ADMIN.booksize;
861                 B.maxoffset = ADMIN.maxoffset;
862             }
863             else
864             {
865                 printf("bad format %s\n", binbookfile);
866                 exit(1);
867             }
868
869         }
870         else
871         {
872             B.bookcount = 0;
873             B.booksize = booksize;
874
875         }
876
877         /* 213: "Book used %d(%d)." */
878         sprintf(msg, CP[213], 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         /* 212: "Can't find book." */
890         ShowMessage(CP[212]);
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