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