Restore previous copyright information that got removed by error.
[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             /* 077: "Illegal move (in check) %s" */
296             puts(CP[77]);
297             bkdisplay(s, cnt, moveno);
298 #endif
299             return false;
300         }
301         else
302         {
303             *mv = (xnode.f << 8) | xnode.t;
304
305             if (is_promoted[board[xnode.t]] )
306                 Balgbr(xnode.f | 0x80, xnode.t, false);
307             else
308                 Balgbr(xnode.f, xnode.t, false);
309
310             return true;
311         }
312     }
313
314     /* Illegal move */
315 #if !defined QUIETBOOKGEN
316     /* 075: "Illegal move (no match)%s\n" */
317     printf(CP[75], s);
318     bkdisplay(s, cnt, moveno);
319 #endif
320     return false;
321 }
322
323
324
325
326 /*
327  * RESET()
328  *
329  * Reset the board and other variables to start a new game.
330  *
331  */
332
333 void
334 RESET(void)
335 {
336     short l;
337
338     flag.illegal = flag.mate = flag.post = flag.quit
339         = flag.reverse = flag.bothsides = flag.onemove = flag.force
340         = false;
341
342     flag.material = flag.coords = flag.hash = flag.easy
343         = flag.beep = flag.rcptr
344         = true;
345
346     flag.stars = flag.shade = flag.back = flag.musttimeout = false;
347     flag.gamein = false;
348     GenCnt   = 0;
349     GameCnt  = 0;
350     CptrFlag[0] = TesujiFlag[0] = false;
351     opponent = black;
352     computer = white;
353
354     for (l = 0; l < NO_SQUARES; l++)
355     {
356         board[l]   = Stboard[l];
357         color[l]   = Stcolor[l];
358         Mvboard[l] = 0;
359     }
360
361     ClearCaptured();
362     InitializeStats();
363     hashbd = hashkey = 0;
364 }
365
366
367
368 static
369 int
370 Vparse (FILE * fd, USHORT *mv, USHORT *flags, USHORT side, int moveno)
371 {
372     int c, i;
373     char s[255];
374
375     *flags = 0;
376
377     while (true)
378     {
379         while (((c = getc(fd)) == ' ')
380                || (c == '!') || (c == '/') || (c == '\n'));
381
382         if (c == '(')
383         {
384             /* amount of time spent for the last move */
385             while (((c = getc(fd)) != ')') && (c != EOF));
386
387             if (c == ')')
388             {
389                 while (((c = getc(fd)) == ' ') || (c == '\n'));
390             }
391         }
392
393         if (c == '[')
394         {
395             /* comment for the actual game */
396             while (((c = getc(fd))) != ']' && (c != EOF));
397
398             if (c == ']')
399             {
400                 while (((c = getc(fd))) == ' ' || (c == '\n'));
401             }
402         }
403
404         if (c == '\r')
405             continue;
406
407         if (c == '#')
408         {
409             /* comment */
410             do
411             {
412                 c = getc(fd);
413
414                 if (c == '\r')
415                     continue;
416                 /* goes to end of line */
417
418                 if (c == '\n')
419                     return 0;
420
421                 if (c == EOF)
422                     return -1;
423             }
424             while (true);
425         }
426
427         s[i = 0] = (char) c;
428
429         while ((c >= '0') && (c <= '9'))
430         {
431             c = getc(fd);
432             s[++i] = (char) c;
433         }
434
435         if (c == '.')
436         {
437             while (((c = getc(fd)) == ' ') || (c == '.') || (c == '\n'));
438             s[i = 0] = (char) c;
439         }
440
441         while (((c = getc(fd)) != '?') && (c != '!') && (c != ' ')
442                && (c != '(') && (c != '\n') && (c != '\t') && (c != EOF))
443         {
444             if (c == '\r')
445                 continue;
446
447             if ((c != 'x') && (c != '-') && (c != ',')
448                 && (c != ';') && (c != '='))
449             {
450                 s[++i] = (char) c;
451             }
452         }
453
454         s[++i] = '\0';
455
456         if (c == '(')
457         {
458             while (((c = getc(fd)) != ')') && (c != EOF));
459
460             if (c == ')')
461                 c = getc(fd);
462         }
463
464         if (c == EOF)
465             return -1;
466
467         if (s[0] == '#')
468         {
469             while ((c != '\n') && (c != EOF))
470                 c = getc(fd);
471
472             if (c == EOF)
473                 return -1;
474             else
475                 return 0;
476         }
477
478         if (strcmp(s, "draw") == 0)
479             continue;
480         else if (strcmp(s, "1-0") == 0)
481             continue;
482         else if (strcmp(s, "0-1") == 0)
483             continue;
484         else if (strcmp(s, "Resigns") == 0)
485             continue;
486         else if (strcmp(s, "Resigns.") == 0)
487             continue;
488         else if (strcmp(s, "Sennichite") == 0)
489             continue;
490         else if (strcmp(s, "Sennichite.") == 0)
491             continue;
492         else if (strcmp(s, "Jishogi") == 0)
493             continue;
494         else if (strcmp(s, "Jishogi.") == 0)
495             continue;
496
497         bhashkey = hashkey;
498         bhashbd  = hashbd;
499
500         i = BVerifyMove(s, mv, moveno);
501
502         if (c == '?')
503         {
504             /* Bad move, not for the program to play */
505             *flags |= BADMOVE;  /* Flag it ! */
506             while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
507         }
508 #ifdef EASY_OPENINGS
509         else if (c == '~')
510         {
511             /* Do not use by computer */
512             *flags |= BADMOVE;  /* Flag it ! */
513
514             while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
515         }
516 #endif
517         else if (c == '!')
518         {
519             /* Good move */
520             *flags |= GOODMOVE; /* Flag it ! */
521
522             while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
523         }
524         else if (c == '\r')
525         {
526             c = getc(fd);
527         }
528
529         if (c == '(' )
530             while (((c = getc(fd)) != ')') && (c != EOF));
531
532         if (!i)
533         {
534             /* flush to start of next */
535             while (((c = getc(fd)) != '#') && (c != EOF));
536
537             if (c == EOF)
538             {
539                 return -1;
540             }
541             else
542             {
543                 ungetc(c, fd);
544                 return i;
545             }
546         }
547
548         return i;
549     }
550 }
551
552
553 static struct gdxadmin ADMIN;
554 struct gdxadmin B;
555 static struct gdxdata DATA;
556
557 /* lts(l) returns most significant 16 bits of l */
558
559 #if SIZEOF_LONG == 8  /* 64-bit long i.e. 8 bytes */
560 #  define lts(x) (USHORT)(((x >> 48) & 0xfffe) | side)
561 #else
562 #  if defined USE_LTSIMP
563 static USHORT ltsimp(long x)
564 {
565     USHORT n;
566     n = (((x >> 16) & 0xfffe));
567     return n;
568 }
569 #    define lts(x) (USHORT)(ltsimp(x) | side)
570 #  else
571 #    define lts(x) (USHORT)(((x >> 16)&0xfffe) | side)
572 #  endif
573 #endif
574
575
576 /* #define HashValue(l) lts(l) */
577 #define HashValue(l) (USHORT)(l & 0xffff)
578
579
580 static int gfd;
581 static ULONG currentoffset;
582
583
584 #define MAXOFFSET(B) ((B.booksize - 1) * sizeof_gdxdata + sizeof_gdxadmin)
585
586 #define HashOffset(hashkey, B) \
587 { \
588   currentoffset = ((ULONG)hashkey % B.booksize) \
589     * sizeof_gdxdata + sizeof_gdxadmin; \
590 }
591
592
593 #define NextOffset(B) \
594 { \
595   currentoffset += sizeof_gdxdata; \
596   if (currentoffset > B.maxoffset) \
597     currentoffset = sizeof_gdxadmin; \
598 }
599
600
601 #define WriteAdmin() \
602 { \
603   lseek(gfd, 0, 0); \
604   write(gfd, (char *)&ADMIN, sizeof_gdxadmin); \
605 }
606
607 #define WriteData() \
608 { \
609   if (mustwrite ) \
610   { \
611     lseek(gfd, currentoffset, 0); \
612     write(gfd, (char *)&DATA, sizeof_gdxdata); \
613     mustwrite = false; \
614   } \
615 }
616
617 static int ReadAdmin(void)
618 {
619     lseek(gfd, 0, 0);
620     return (sizeof_gdxadmin == read(gfd, (char *)&ADMIN, sizeof_gdxadmin));
621 }
622
623 static int ReadData(struct gdxdata *DATA)
624 {
625     lseek(gfd, currentoffset, 0);
626     return (sizeof_gdxdata == read(gfd, (char *)DATA, sizeof_gdxdata));
627 }
628
629
630 /*
631  * GetOpenings()
632  *
633  * CHECKME: is this still valid esp. wrt gnushogi.book?
634  *
635  * Read in the Opening Book file and parse the algebraic notation for a move
636  * into an unsigned integer format indicating the from and to square. Create
637  * a linked list of opening lines of play, with entry->next pointing to the
638  * next line and entry->move pointing to a chunk of memory containing the
639  * moves. More Opening lines of up to 100 half moves may be added to
640  * gnushogi.book. But now it's a hashed table by position which yields a move
641  * or moves for each position. It no longer knows about openings per se only
642  * positions and recommended moves in those positions.
643  *
644  */
645
646 void
647 GetOpenings(void)
648 {
649     short i;
650     int mustwrite = false, first;
651     unsigned short xside, side;
652     short c;
653     USHORT mv, flags;
654     unsigned int x;
655     unsigned int games = 0;
656     LONG collisions = 0;
657     char msg[80];
658
659     FILE *fd;
660
661     if ((fd = fopen(bookfile, "r")) == NULL)
662         fd = fopen("gnushogi.tbk", "r");
663
664     if (fd != NULL)
665     {
666         /* yes add to book */
667         /* open book as writer */
668         gfd = open(binbookfile, O_RDONLY | O_BINARY);
669
670         if (gfd >= 0)
671         {
672             if (ReadAdmin())
673             {
674                 B.bookcount = ADMIN.bookcount;
675                 B.booksize = ADMIN.booksize;
676                 B.maxoffset = ADMIN.maxoffset;
677
678                 if (B.booksize && !(B.maxoffset == MAXOFFSET(B)))
679                 {
680                     printf("bad format %s\n", binbookfile);
681                     exit(1);
682                 }
683             }
684             else
685             {
686                 printf("bad format %s\n", binbookfile);
687                 exit(1);
688             }
689             close(gfd);
690             gfd = open(binbookfile, O_RDWR | O_BINARY);
691
692         }
693         else
694         {
695             gfd = open(binbookfile, O_RDWR | O_CREAT | O_BINARY, 0644);
696
697             ADMIN.bookcount = B.bookcount = 0;
698             ADMIN.booksize = B.booksize = booksize;
699             B.maxoffset = ADMIN.maxoffset = MAXOFFSET(B);
700             DATA.hashbd = 0;
701             DATA.hashkey = 0;
702             DATA.bmove = 0;
703             DATA.flags = 0;
704             DATA.hint = 0;
705             DATA.count = 0;
706             write(gfd, (char *)&ADMIN, sizeof_gdxadmin);
707             printf("creating bookfile %s %ld %ld\n",
708                     binbookfile, B.maxoffset, B.booksize);
709
710             for (x = 0; x < B.booksize; x++)
711             {
712                 write(gfd, (char *)&DATA, sizeof_gdxdata);
713             }
714         }
715
716         if (gfd >= 0)
717         {
718             /* setvbuf(fd, buffr, _IOFBF, 2048); */
719             side = black;
720             xside = white;
721             hashbd = hashkey = 0;
722             i = 0;
723
724             while ((c = Vparse(fd, &mv, &flags, side, i)) >= 0)
725             {
726                 if (c == 1)
727                 {
728                     /*
729                      * If this is not the first move of an opening and
730                      * if it's the first time we have seen it then
731                      * save the next move as a hint.
732                      */
733                     i++;
734
735                     if (i < bookmaxply + 2)
736                     {
737                         if (i > 1 && !(flags & BADMOVE))
738                             DATA.hint = mv;
739
740                         if (i < bookmaxply + 1)
741                         {
742                             /*
743                              * See if this position and move already
744                              * exist from some other opening.
745                              */
746
747                             WriteData();
748                             HashOffset(bhashkey, B);
749                             first = true;
750
751                             while (true)
752                             {
753                                 if (!ReadData(&DATA))
754                                     break; /* corrupted binbook file */
755
756                                 if (DATA.bmove == 0)
757                                     break;  /* free entry */
758
759                                 if (DATA.hashkey == HashValue(bhashkey)
760                                     && DATA.hashbd == bhashbd)
761                                 {
762                                     if (DATA.bmove == mv)
763                                     {
764                                         /*
765                                          * Yes, so just bump count - count
766                                          * is used to choose the opening
767                                          * move in proportion to its
768                                          * presence in the book.
769                                          */
770
771                                         DATA.count++;
772                                         DATA.flags |= flags;
773                                         mustwrite = true;
774                                         break;
775                                     }
776                                     else
777                                     {
778                                         if (first)
779                                             collisions++;
780
781                                         if (DATA.flags & LASTMOVE)
782                                         {
783                                             DATA.flags &= (~LASTMOVE);
784                                             mustwrite = true;
785                                             WriteData();
786                                         }
787                                     }
788                                 }
789
790                                 NextOffset(B);
791                                 first = false;
792                             }
793
794                             /*
795                              * Doesn't exist so add it to the book.
796                              */
797
798                             if (!mustwrite)
799                             {
800                                 B.bookcount++;
801
802                                 if ((B.bookcount % 1000) == 0)
803                                 {
804                                     /* CHECKME: may want to get rid of this,
805                                      * especially for xshogi. */
806                                     printf("%ld rec %d openings "
807                                            "processed\n",
808                                            B.bookcount, games);
809                                 }
810
811                                 /* initialize a record */
812                                 DATA.hashbd = bhashbd;
813                                 DATA.hashkey = HashValue(bhashkey);
814                                 DATA.bmove = mv;
815                                 DATA.flags = flags | LASTMOVE;
816                                 DATA.count = 1;
817                                 DATA.hint = 0;
818                                 mustwrite = true;
819                             }
820                         }
821                     }
822
823                     computer = opponent;
824                     opponent = computer ^ 1;
825
826                     xside = side;
827                     side = side ^ 1;
828                 }
829                 else if (i > 0)
830                 {
831                     /* reset for next opening */
832                     games++;
833                     WriteData();
834                     RESET();
835                     i = 0;
836                     side = black;
837                     xside = white;
838
839                 }
840             }
841
842             WriteData();
843             fclose(fd);
844             /* write admin rec with counts */
845             ADMIN.bookcount = B.bookcount;
846             WriteAdmin();
847
848             close(gfd);
849         }
850     }
851
852     if (binbookfile != NULL)
853     {
854         /* open book as reader */
855         gfd = open(binbookfile, O_RDONLY | O_BINARY);
856
857         if (gfd >= 0)
858         {
859             if (ReadAdmin() && (!ADMIN.booksize
860                                 || (ADMIN.maxoffset == MAXOFFSET(ADMIN))))
861             {
862                 B.bookcount = ADMIN.bookcount;
863                 B.booksize  = ADMIN.booksize;
864                 B.maxoffset = ADMIN.maxoffset;
865             }
866             else
867             {
868                 printf("bad format %s\n", binbookfile);
869                 exit(1);
870             }
871
872         }
873         else
874         {
875             B.bookcount = 0;
876             B.booksize = booksize;
877
878         }
879
880         /* 213: "Book used %d(%d)." */
881         sprintf(msg, CP[213], B.bookcount, B.booksize);
882         ShowMessage(msg);
883     }
884
885     /* Set everything back to start the game. */
886     Book = BOOKFAIL;
887     RESET();
888
889     /* Now get ready to play .*/
890     if (!B.bookcount)
891     {
892         /* 212: "Can't find book." */
893         ShowMessage(CP[212]);
894         Book = 0;
895     }
896 }
897
898
899
900 /*
901  * OpeningBook(hint, side)
902  *
903  * Go through each of the opening lines of play and check for a match with
904  * the current game listing. If a match occurs, generate a random
905  * number. If this number is the largest generated so far then the next
906  * move in this line becomes the current "candidate".  After all lines are
907  * checked, the candidate move is put at the top of the Tree[] array and
908  * will be played by the program.  Note that the program does not handle
909  * book transpositions.
910  */
911
912 int
913 OpeningBook(unsigned short *hint, short side)
914 {
915     unsigned short r, m;
916     int possibles = TrPnt[2] - TrPnt[1];
917
918     gsrand((unsigned int) time((long *) 0));
919     m = 0;
920
921     /*
922      * Find all the moves for this position  - count them and get their
923      * total count.
924      */
925
926     {
927         USHORT i, x;
928         USHORT rec = 0;
929         USHORT summ = 0;
930         USHORT h = 0, b = 0;
931         struct gdxdata OBB[128];
932
933         if (B.bookcount == 0)
934         {
935             Book--;
936             return false;
937         }
938
939         x = 0;
940         HashOffset(hashkey, B);
941 #ifdef BOOKTEST
942         printf("looking for book move, bhashbd = 0x%lx bhashkey = 0x%x\n",
943                (ULONG)hashbd, HashValue(hashkey));
944 #endif
945         while (true)
946         {
947             if (!ReadData(&OBB[x]))
948                 break;
949
950             if (OBB[x].bmove == 0)
951                 break;
952
953 #ifdef BOOKTEST
954             printf("compare with bhashbd = 0x%lx bhashkey = 0x%x\n",
955                    OBB[x].hashbd, OBB[x].hashkey);
956 #endif
957             if ((OBB[x].hashkey == HashValue(hashkey))
958                 && (OBB[x].hashbd == (ULONG)hashbd))
959             {
960                 x++;
961
962                 if (OBB[x-1].flags & LASTMOVE)
963                     break;
964             }
965
966             NextOffset(B);
967         }
968
969 #ifdef BOOKTEST
970         printf("%d book move(s) found.\n", x);
971 #endif
972
973         if (x == 0)
974         {
975             Book--;
976             return false;
977         }
978
979         for (i = 0; i < x; i++)
980         {
981             if (OBB[i].flags & BADMOVE)
982             {
983                 m = OBB[i].bmove;
984
985                 /* Is the move in the MoveList? */
986                 for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
987                 {
988                     if (((Tree[b].f << 8) | Tree[b].t) == m)
989                     {
990                         if (--possibles)
991                             Tree[b].score = DONTUSE;
992                         break;
993                     }
994                 }
995             }
996             else
997             {
998 #if defined BOOKTEST
999                 char s[20];
1000                 movealgbr(m = OBB[i].bmove, s);
1001                 printf("finding book move: %s\n", s);
1002 #endif
1003                 summ += OBB[i].count;
1004             }
1005         }
1006
1007         if (summ == 0)
1008         {
1009             Book--;
1010             return false;
1011         }
1012
1013         r = (urand() % summ);
1014
1015         for (i = 0; i < x; i++)
1016         {
1017             if (!(OBB[i].flags & BADMOVE))
1018             {
1019                 if (r < OBB[i].count)
1020                 {
1021                     rec = i;
1022                     break;
1023                 }
1024                 else
1025                 {
1026                     r -= OBB[i].count;
1027                 }
1028             }
1029         }
1030
1031         h = OBB[rec].hint;
1032         m = OBB[rec].bmove;
1033
1034         /* Make sure the move is in the MoveList. */
1035         for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
1036         {
1037             if (((Tree[b].f << 8) | Tree[b].t) == m)
1038             {
1039                 Tree[b].flags |= book;
1040                 Tree[b].score = 0;
1041                 break;
1042             }
1043         }
1044
1045         /* Make sure it's the best. */
1046
1047         pick(TrPnt[1], TrPnt[2] - 1);
1048
1049         if (Tree[TrPnt[1]].score)
1050         {
1051             /* no! */
1052             Book--;
1053             return false;
1054         }
1055
1056         /* Ok, pick up the hint and go. */
1057         *hint = h;
1058         return true;
1059     }
1060
1061     Book--;
1062     return false;
1063 }
1064
1065
1066