Changing license to GPL3 (and bumping version to 1.4.0).
[gnushogi.git] / gnushogi / book.c
blobcadefa3a63804726bb1411d5ec82d528b36b7b1d
1 /*
2 * FILE: book.c
4 * ----------------------------------------------------------------------
6 * Copyright (c) 2012 Free Software Foundation
8 * GNU SHOGI is based on GNU CHESS
10 * This file is part of GNU SHOGI.
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.
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.
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 * ----------------------------------------------------------------------
29 #include "gnushogi.h"
31 #define O_BINARY 0
33 #if HAVE_UNISTD_H
34 /* Declarations of read(), write(), close(), and lseek(). */
35 #include <unistd.h>
36 #endif
38 #if HAVE_FCNTL_H
39 #include <fcntl.h>
40 #endif
42 #include "book.h"
44 unsigned booksize = BOOKSIZE;
45 unsigned short bookmaxply = BOOKMAXPLY;
46 unsigned bookcount = 0;
48 #ifdef BOOK
49 char *bookfile = BOOK;
50 #else
51 char *bookfile = NULL;
52 #endif
54 #ifdef BINBOOK
55 char *binbookfile = BINBOOK;
56 #else
57 char *binbookfile = NULL;
58 #endif
60 static char bmvstr[3][7];
62 static ULONG bhashbd;
63 static ULONG bhashkey;
67 * Balgbr(f, t, flag)
69 * Generate move strings in different formats.
72 void
73 Balgbr(short f, short t, short flag)
75 short promoted = false;
77 if ((f & 0x80) != 0)
79 f &= 0x7f;
80 promoted = true;
83 if (f > NO_SQUARES)
85 short piece;
86 piece = f - NO_SQUARES;
88 if (f > (NO_SQUARES + NO_PIECES))
89 piece -= NO_PIECES;
91 flag = (dropmask | piece);
94 if ((t & 0x80) != 0)
96 flag |= promote;
97 t &= 0x7f;
100 if ((f == t) && ((f != 0) || (t != 0)))
103 * error in algbr: FROM=TO=t
106 bmvstr[0][0] = bmvstr[1][0] = bmvstr[2][0] = '\0';
108 else
110 if ((flag & dropmask) != 0)
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] = '\'';
122 else
124 if ((f != 0) || (t != 0))
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';
135 if (promoted)
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]);
142 else
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]);
149 if (flag & promote)
151 strcat(bmvstr[0], "+");
152 strcat(bmvstr[1], "+");
153 strcat(bmvstr[2], "+");
156 else
158 bmvstr[0][0] = bmvstr[1][0] = bmvstr[2][0] = '\0';
167 #ifndef QUIETBOOKGEN
168 void
169 bkdisplay(char *s, int cnt, int moveno)
171 static short pnt;
172 struct leaf *node;
173 int r, c, l;
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");
180 #ifndef SEMIQUIETBOOKGEN
181 printf("legal moves are \n");
183 while (pnt < TrPnt[3])
185 node = &Tree[pnt++];
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);
192 printf("%s %s %s\n",
193 bmvstr[0], bmvstr[1], bmvstr[2]);
196 printf("\n current board is\n");
198 for (r = (NO_ROWS - 1); r >= 0; r--)
200 for (c = 0; c <= (NO_COLS - 1); c++)
202 char pc;
204 l = locn(r, c);
205 pc = (is_promoted[board[l]] ? '+' : ' ');
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]]);
215 printf("\n");
218 printf("\n");
220 short color;
222 for (color = black; color <= white; color++)
224 short piece, c;
226 printf((color == black) ? "black " : "white ");
228 for (piece = pawn; piece <= king; piece++)
230 if ((c = Captured[color][piece]))
231 printf("%i%c ", c, pxx[piece]);
234 printf("\n");
237 #endif /* SEMIQUIETBOOKGEN */
240 #endif /* QUIETBOOKGEN */
245 * BVerifyMove(s, mv, moveno)
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.
252 BVerifyMove(char *s, unsigned short *mv, int moveno)
254 static short pnt, tempb, tempc, tempsf, tempst, cnt;
255 static struct leaf xnode;
256 struct leaf *node;
258 *mv = 0;
259 cnt = 0;
260 MoveList(opponent, 2, -2, true);
261 pnt = TrPnt[2];
263 while (pnt < TrPnt[3])
265 node = &Tree[pnt++];
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);
272 if (strcmp(s, bmvstr[0]) == 0 || strcmp(s, bmvstr[1]) == 0 ||
273 strcmp(s, bmvstr[2]) == 0)
275 cnt++;
276 xnode = *node;
280 if (cnt == 1)
282 short blockable;
284 MakeMove(opponent, &xnode, &tempb,
285 &tempc, &tempsf, &tempst, &INCscore);
287 if (SqAttacked(PieceList[opponent][0], computer, &blockable))
289 UnmakeMove(opponent, &xnode, &tempb, &tempc, &tempsf, &tempst);
290 /* Illegal move in check */
291 #if !defined QUIETBOOKGEN
292 /* 077: "Illegal move (in check) %s" */
293 printf(CP[77]);
294 printf("\n");
295 bkdisplay(s, cnt, moveno);
296 #endif
297 return false;
299 else
301 *mv = (xnode.f << 8) | xnode.t;
303 if (is_promoted[board[xnode.t]] )
304 Balgbr(xnode.f | 0x80, xnode.t, false);
305 else
306 Balgbr(xnode.f, xnode.t, false);
308 return true;
312 /* Illegal move */
313 #if !defined QUIETBOOKGEN
314 /* 075: "Illegal move (no match)%s\n" */
315 printf(CP[75], s);
316 bkdisplay(s, cnt, moveno);
317 #endif
318 return false;
325 * RESET()
327 * Reset the board and other variables to start a new game.
331 void
332 RESET(void)
334 short l;
336 flag.illegal = flag.mate = flag.post = flag.quit
337 = flag.reverse = flag.bothsides = flag.onemove = flag.force
338 = false;
340 flag.material = flag.coords = flag.hash = flag.easy
341 = flag.beep = flag.rcptr
342 = true;
344 flag.stars = flag.shade = flag.back = flag.musttimeout = false;
345 flag.gamein = false;
346 GenCnt = 0;
347 GameCnt = 0;
348 CptrFlag[0] = TesujiFlag[0] = false;
349 opponent = black;
350 computer = white;
352 for (l = 0; l < NO_SQUARES; l++)
354 board[l] = Stboard[l];
355 color[l] = Stcolor[l];
356 Mvboard[l] = 0;
359 ClearCaptured();
360 InitializeStats();
361 hashbd = hashkey = 0;
366 static
368 Vparse (FILE * fd, USHORT *mv, USHORT *flags, USHORT side, int moveno)
370 int c, i;
371 char s[255];
373 *flags = 0;
375 while (true)
377 while (((c = getc(fd)) == ' ')
378 || (c == '!') || (c == '/') || (c == '\n'));
380 if (c == '(')
382 /* amount of time spent for the last move */
383 while (((c = getc(fd)) != ')') && (c != EOF));
385 if (c == ')')
387 while (((c = getc(fd)) == ' ') || (c == '\n'));
391 if (c == '[')
393 /* comment for the actual game */
394 while (((c = getc(fd))) != ']' && (c != EOF));
396 if (c == ']')
398 while (((c = getc(fd))) == ' ' || (c == '\n'));
402 if (c == '\r')
403 continue;
405 if (c == '#')
407 /* comment */
410 c = getc(fd);
412 if (c == '\r')
413 continue;
414 /* goes to end of line */
416 if (c == '\n')
417 return 0;
419 if (c == EOF)
420 return -1;
422 while (true);
425 s[i = 0] = (char) c;
427 while ((c >= '0') && (c <= '9'))
429 c = getc(fd);
430 s[++i] = (char) c;
433 if (c == '.')
435 while (((c = getc(fd)) == ' ') || (c == '.') || (c == '\n'));
436 s[i = 0] = (char) c;
439 while (((c = getc(fd)) != '?') && (c != '!') && (c != ' ')
440 && (c != '(') && (c != '\n') && (c != '\t') && (c != EOF))
442 if (c == '\r')
443 continue;
445 if ((c != 'x') && (c != '-') && (c != ',')
446 && (c != ';') && (c != '='))
448 s[++i] = (char) c;
452 s[++i] = '\0';
454 if (c == '(')
456 while (((c = getc(fd)) != ')') && (c != EOF));
458 if (c == ')')
459 c = getc(fd);
462 if (c == EOF)
463 return -1;
465 if (s[0] == '#')
467 while ((c != '\n') && (c != EOF))
468 c = getc(fd);
470 if (c == EOF)
471 return -1;
472 else
473 return 0;
476 if (strcmp(s, "draw") == 0)
477 continue;
478 else if (strcmp(s, "1-0") == 0)
479 continue;
480 else if (strcmp(s, "0-1") == 0)
481 continue;
482 else if (strcmp(s, "Resigns") == 0)
483 continue;
484 else if (strcmp(s, "Resigns.") == 0)
485 continue;
486 else if (strcmp(s, "Sennichite") == 0)
487 continue;
488 else if (strcmp(s, "Sennichite.") == 0)
489 continue;
490 else if (strcmp(s, "Jishogi") == 0)
491 continue;
492 else if (strcmp(s, "Jishogi.") == 0)
493 continue;
495 bhashkey = hashkey;
496 bhashbd = hashbd;
498 i = BVerifyMove(s, mv, moveno);
500 if (c == '?')
502 /* Bad move, not for the program to play */
503 *flags |= BADMOVE; /* Flag it ! */
504 while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
506 #ifdef EASY_OPENINGS
507 else if (c == '~')
509 /* Do not use by computer */
510 *flags |= BADMOVE; /* Flag it ! */
512 while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
514 #endif
515 else if (c == '!')
517 /* Good move */
518 *flags |= GOODMOVE; /* Flag it ! */
520 while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
522 else if (c == '\r')
524 c = getc(fd);
527 if (c == '(' )
528 while (((c = getc(fd)) != ')') && (c != EOF));
530 if (!i)
532 /* flush to start of next */
533 while (((c = getc(fd)) != '#') && (c != EOF));
535 if (c == EOF)
537 return -1;
539 else
541 ungetc(c, fd);
542 return i;
546 return i;
551 static struct gdxadmin ADMIN;
552 struct gdxadmin B;
553 static struct gdxdata DATA;
555 /* lts(l) returns most significant 16 bits of l */
557 #if SIZEOF_LONG == 8 /* 64-bit long i.e. 8 bytes */
558 # define lts(x) (USHORT)(((x >> 48) & 0xfffe) | side)
559 #else
560 # if defined USE_LTSIMP
561 static USHORT ltsimp(long x)
563 USHORT n;
564 n = (((x >> 16) & 0xfffe));
565 return n;
567 # define lts(x) (USHORT)(ltsimp(x) | side)
568 # else
569 # define lts(x) (USHORT)(((x >> 16)&0xfffe) | side)
570 # endif
571 #endif
574 /* #define HashValue(l) lts(l) */
575 #define HashValue(l) (USHORT)(l & 0xffff)
578 static int gfd;
579 static ULONG currentoffset;
582 #define MAXOFFSET(B) ((B.booksize - 1) * sizeof_gdxdata + sizeof_gdxadmin)
584 #define HashOffset(hashkey, B) \
586 currentoffset = ((ULONG)hashkey % B.booksize) \
587 * sizeof_gdxdata + sizeof_gdxadmin; \
591 #define NextOffset(B) \
593 currentoffset += sizeof_gdxdata; \
594 if (currentoffset > B.maxoffset) \
595 currentoffset = sizeof_gdxadmin; \
599 #define WriteAdmin() \
601 lseek(gfd, 0, 0); \
602 write(gfd, (char *)&ADMIN, sizeof_gdxadmin); \
605 #define WriteData() \
607 if (mustwrite ) \
609 lseek(gfd, currentoffset, 0); \
610 write(gfd, (char *)&DATA, sizeof_gdxdata); \
611 mustwrite = false; \
615 static int ReadAdmin(void)
617 lseek(gfd, 0, 0);
618 return (sizeof_gdxadmin == read(gfd, (char *)&ADMIN, sizeof_gdxadmin));
621 static int ReadData(struct gdxdata *DATA)
623 lseek(gfd, currentoffset, 0);
624 return (sizeof_gdxdata == read(gfd, (char *)DATA, sizeof_gdxdata));
629 * GetOpenings()
631 * CHECKME: is this still valid esp. wrt gnushogi.book?
633 * Read in the Opening Book file and parse the algebraic notation for a move
634 * into an unsigned integer format indicating the from and to square. Create
635 * a linked list of opening lines of play, with entry->next pointing to the
636 * next line and entry->move pointing to a chunk of memory containing the
637 * moves. More Opening lines of up to 100 half moves may be added to
638 * gnushogi.book. But now it's a hashed table by position which yields a move
639 * or moves for each position. It no longer knows about openings per se only
640 * positions and recommended moves in those positions.
644 void
645 GetOpenings(void)
647 short i;
648 int mustwrite = false, first;
649 unsigned short xside, side;
650 short c;
651 USHORT mv, flags;
652 unsigned int x;
653 unsigned int games = 0;
654 LONG collisions = 0;
655 char msg[80];
657 FILE *fd;
659 if ((fd = fopen(bookfile, "r")) == NULL)
660 fd = fopen("gnushogi.tbk", "r");
662 if (fd != NULL)
664 /* yes add to book */
665 /* open book as writer */
666 gfd = open(binbookfile, O_RDONLY | O_BINARY);
668 if (gfd >= 0)
670 if (ReadAdmin())
672 B.bookcount = ADMIN.bookcount;
673 B.booksize = ADMIN.booksize;
674 B.maxoffset = ADMIN.maxoffset;
676 if (B.booksize && !(B.maxoffset == MAXOFFSET(B)))
678 printf("bad format %s\n", binbookfile);
679 exit(1);
682 else
684 printf("bad format %s\n", binbookfile);
685 exit(1);
687 close(gfd);
688 gfd = open(binbookfile, O_RDWR | O_BINARY);
691 else
693 gfd = open(binbookfile, O_RDWR | O_CREAT | O_BINARY, 0644);
695 ADMIN.bookcount = B.bookcount = 0;
696 ADMIN.booksize = B.booksize = booksize;
697 B.maxoffset = ADMIN.maxoffset = MAXOFFSET(B);
698 DATA.hashbd = 0;
699 DATA.hashkey = 0;
700 DATA.bmove = 0;
701 DATA.flags = 0;
702 DATA.hint = 0;
703 DATA.count = 0;
704 write(gfd, (char *)&ADMIN, sizeof_gdxadmin);
705 printf("creating bookfile %s %ld %ld\n",
706 binbookfile, B.maxoffset, B.booksize);
708 for (x = 0; x < B.booksize; x++)
710 write(gfd, (char *)&DATA, sizeof_gdxdata);
714 if (gfd >= 0)
716 /* setvbuf(fd, buffr, _IOFBF, 2048); */
717 side = black;
718 xside = white;
719 hashbd = hashkey = 0;
720 i = 0;
722 while ((c = Vparse(fd, &mv, &flags, side, i)) >= 0)
724 if (c == 1)
727 * If this is not the first move of an opening and
728 * if it's the first time we have seen it then
729 * save the next move as a hint.
731 i++;
733 if (i < bookmaxply + 2)
735 if (i > 1 && !(flags & BADMOVE))
736 DATA.hint = mv;
738 if (i < bookmaxply + 1)
741 * See if this position and move already
742 * exist from some other opening.
745 WriteData();
746 HashOffset(bhashkey, B);
747 first = true;
749 while (true)
751 if (!ReadData(&DATA))
752 break; /* corrupted binbook file */
754 if (DATA.bmove == 0)
755 break; /* free entry */
757 if (DATA.hashkey == HashValue(bhashkey)
758 && DATA.hashbd == bhashbd)
760 if (DATA.bmove == mv)
763 * Yes, so just bump count - count
764 * is used to choose the opening
765 * move in proportion to its
766 * presence in the book.
769 DATA.count++;
770 DATA.flags |= flags;
771 mustwrite = true;
772 break;
774 else
776 if (first)
777 collisions++;
779 if (DATA.flags & LASTMOVE)
781 DATA.flags &= (~LASTMOVE);
782 mustwrite = true;
783 WriteData();
788 NextOffset(B);
789 first = false;
793 * Doesn't exist so add it to the book.
796 if (!mustwrite)
798 B.bookcount++;
800 if ((B.bookcount % 1000) == 0)
802 /* CHECKME: may want to get rid of this,
803 * especially for xshogi. */
804 printf("%ld rec %d openings "
805 "processed\n",
806 B.bookcount, games);
809 /* initialize a record */
810 DATA.hashbd = bhashbd;
811 DATA.hashkey = HashValue(bhashkey);
812 DATA.bmove = mv;
813 DATA.flags = flags | LASTMOVE;
814 DATA.count = 1;
815 DATA.hint = 0;
816 mustwrite = true;
821 computer = opponent;
822 opponent = computer ^ 1;
824 xside = side;
825 side = side ^ 1;
827 else if (i > 0)
829 /* reset for next opening */
830 games++;
831 WriteData();
832 RESET();
833 i = 0;
834 side = black;
835 xside = white;
840 WriteData();
841 fclose(fd);
842 /* write admin rec with counts */
843 ADMIN.bookcount = B.bookcount;
844 WriteAdmin();
846 close(gfd);
850 if (binbookfile != NULL)
852 /* open book as reader */
853 gfd = open(binbookfile, O_RDONLY | O_BINARY);
855 if (gfd >= 0)
857 if (ReadAdmin() && (!ADMIN.booksize
858 || (ADMIN.maxoffset == MAXOFFSET(ADMIN))))
860 B.bookcount = ADMIN.bookcount;
861 B.booksize = ADMIN.booksize;
862 B.maxoffset = ADMIN.maxoffset;
864 else
866 printf("bad format %s\n", binbookfile);
867 exit(1);
871 else
873 B.bookcount = 0;
874 B.booksize = booksize;
878 /* 213: "Book used %d(%d)." */
879 sprintf(msg, CP[213], B.bookcount, B.booksize);
880 ShowMessage(msg);
883 /* Set everything back to start the game. */
884 Book = BOOKFAIL;
885 RESET();
887 /* Now get ready to play .*/
888 if (!B.bookcount)
890 /* 212: "Can't find book." */
891 ShowMessage(CP[212]);
892 Book = 0;
899 * OpeningBook(hint, side)
901 * Go through each of the opening lines of play and check for a match with
902 * the current game listing. If a match occurs, generate a random
903 * number. If this number is the largest generated so far then the next
904 * move in this line becomes the current "candidate". After all lines are
905 * checked, the candidate move is put at the top of the Tree[] array and
906 * will be played by the program. Note that the program does not handle
907 * book transpositions.
911 OpeningBook(unsigned short *hint, short side)
913 unsigned short r, m;
914 int possibles = TrPnt[2] - TrPnt[1];
916 gsrand((unsigned int) time((long *) 0));
917 m = 0;
920 * Find all the moves for this position - count them and get their
921 * total count.
925 USHORT i, x;
926 USHORT rec = 0;
927 USHORT summ = 0;
928 USHORT h = 0, b = 0;
929 struct gdxdata OBB[128];
931 if (B.bookcount == 0)
933 Book--;
934 return false;
937 x = 0;
938 HashOffset(hashkey, B);
939 #ifdef BOOKTEST
940 printf("looking for book move, bhashbd = 0x%lx bhashkey = 0x%x\n",
941 (ULONG)hashbd, HashValue(hashkey));
942 #endif
943 while (true)
945 if (!ReadData(&OBB[x]))
946 break;
948 if (OBB[x].bmove == 0)
949 break;
951 #ifdef BOOKTEST
952 printf("compare with bhashbd = 0x%lx bhashkey = 0x%x\n",
953 OBB[x].hashbd, OBB[x].hashkey);
954 #endif
955 if ((OBB[x].hashkey == HashValue(hashkey))
956 && (OBB[x].hashbd == (ULONG)hashbd))
958 x++;
960 if (OBB[x-1].flags & LASTMOVE)
961 break;
964 NextOffset(B);
967 #ifdef BOOKTEST
968 printf("%d book move(s) found.\n", x);
969 #endif
971 if (x == 0)
973 Book--;
974 return false;
977 for (i = 0; i < x; i++)
979 if (OBB[i].flags & BADMOVE)
981 m = OBB[i].bmove;
983 /* Is the move in the MoveList? */
984 for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
986 if (((Tree[b].f << 8) | Tree[b].t) == m)
988 if (--possibles)
989 Tree[b].score = DONTUSE;
990 break;
994 else
996 #if defined BOOKTEST
997 char s[20];
998 movealgbr(m = OBB[i].bmove, s);
999 printf("finding book move: %s\n", s);
1000 #endif
1001 summ += OBB[i].count;
1005 if (summ == 0)
1007 Book--;
1008 return false;
1011 r = (urand() % summ);
1013 for (i = 0; i < x; i++)
1015 if (!(OBB[i].flags & BADMOVE))
1017 if (r < OBB[i].count)
1019 rec = i;
1020 break;
1022 else
1024 r -= OBB[i].count;
1029 h = OBB[rec].hint;
1030 m = OBB[rec].bmove;
1032 /* Make sure the move is in the MoveList. */
1033 for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
1035 if (((Tree[b].f << 8) | Tree[b].t) == m)
1037 Tree[b].flags |= book;
1038 Tree[b].score = 0;
1039 break;
1043 /* Make sure it's the best. */
1045 pick(TrPnt[1], TrPnt[2] - 1);
1047 if (Tree[TrPnt[1]].score)
1049 /* no! */
1050 Book--;
1051 return false;
1054 /* Ok, pick up the hint and go. */
1055 *hint = h;
1056 return true;
1059 Book--;
1060 return false;