Merge branch 'maint'
[gnushogi.git] / gnushogi / book.c
blob478e69f3d9e536b62eaca23d932b915097b625a4
1 /*
2 * FILE: book.c
4 * ----------------------------------------------------------------------
5 * Copyright (c) 1993, 1994, 1995 Matthias Mutz
6 * Copyright (c) 1999 Michael Vanier and the Free Software Foundation
7 * Copyright (c) 2008, 2013, 2014 Yann Dirson and the Free Software Foundation
9 * GNU SHOGI is based on GNU CHESS
11 * Copyright (c) 1988, 1989, 1990 John Stanback
12 * Copyright (c) 1992 Free Software Foundation
14 * This file is part of GNU SHOGI.
16 * GNU Shogi is free software; you can redistribute it and/or modify it
17 * under the terms of the GNU General Public License as published by the
18 * Free Software Foundation; either version 3 of the License,
19 * or (at your option) any later version.
21 * GNU Shogi is distributed in the hope that it will be useful, but WITHOUT
22 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 * for more details.
26 * You should have received a copy of the GNU General Public License along
27 * with GNU Shogi; see the file COPYING. If not, see
28 * <http://www.gnu.org/licenses/>.
29 * ----------------------------------------------------------------------
33 #include "gnushogi.h"
35 #define O_BINARY 0
37 #if HAVE_UNISTD_H
38 /* Declarations of read(), write(), close(), and lseek(). */
39 #include <unistd.h>
40 #endif
42 #if HAVE_FCNTL_H
43 #include <fcntl.h>
44 #endif
46 #include "book.h"
48 unsigned booksize = BOOKSIZE;
49 unsigned short bookmaxply = BOOKMAXPLY;
50 unsigned bookcount = 0;
52 #ifdef BOOK
53 char *bookfile = BOOK;
54 #else
55 char *bookfile = NULL;
56 #endif
58 #ifdef BINBOOK
59 char *binbookfile = BINBOOK;
60 #else
61 char *binbookfile = NULL;
62 #endif
64 static char bmvstr[3][7];
66 static ULONG bhashbd;
67 static ULONG bhashkey;
71 * Balgbr(f, t, flags)
73 * Generate move strings in different formats.
76 static void
77 Balgbr(short f, short t, short flags)
79 short promoted = false;
81 if ((f & 0x80) != 0)
83 f &= 0x7f;
84 promoted = true;
87 if (f > NO_SQUARES)
89 short piece;
90 piece = f - NO_SQUARES;
92 if (f > (NO_SQUARES + NO_PIECES))
93 piece -= NO_PIECES;
95 flags = (dropmask | piece);
98 if ((t & 0x80) != 0)
100 flags |= promote;
101 t &= 0x7f;
104 if ((f == t) && ((f != 0) || (t != 0)))
107 * error in algbr: FROM=TO=t
110 bmvstr[0][0] = bmvstr[1][0] = bmvstr[2][0] = '\0';
112 else
114 if ((flags & dropmask) != 0)
116 /* bmvstr[0]: P*3c bmvstr[1]: P'3c */
117 short piece = flags & pmask;
118 bmvstr[0][0] = pxx[piece];
119 bmvstr[0][1] = '*';
120 bmvstr[0][2] = COL_NAME(column(t));
121 bmvstr[0][3] = ROW_NAME(row(t));
122 bmvstr[0][4] = bmvstr[2][0] = '\0';
123 strcpy(bmvstr[1], bmvstr[0]);
124 bmvstr[1][1] = '\'';
126 else
128 if ((f != 0) || (t != 0))
130 /* algebraic notation */
131 /* bmvstr[0]: 7g7f bmvstr[1]:
132 * (+)P7g7f(+) bmvstr[2]: (+)P7f(+) */
133 bmvstr[0][0] = COL_NAME(column(f));
134 bmvstr[0][1] = ROW_NAME(row(f));
135 bmvstr[0][2] = COL_NAME(column(t));
136 bmvstr[0][3] = ROW_NAME(row(t));
137 bmvstr[0][4] = '\0';
139 if (promoted)
141 bmvstr[1][0] = bmvstr[2][0] = '+';
142 bmvstr[1][1] = bmvstr[2][1] = pxx[board[f]];
143 strcpy(&bmvstr[1][2], &bmvstr[0][0]);
144 strcpy(&bmvstr[2][2], &bmvstr[0][2]);
146 else
148 bmvstr[1][0] = bmvstr[2][0] = pxx[board[f]];
149 strcpy(&bmvstr[1][1], &bmvstr[0][0]);
150 strcpy(&bmvstr[2][1], &bmvstr[0][2]);
153 if (flags & promote)
155 strcat(bmvstr[0], "+");
156 strcat(bmvstr[1], "+");
157 strcat(bmvstr[2], "+");
160 else
162 bmvstr[0][0] = bmvstr[1][0] = bmvstr[2][0] = '\0';
169 #ifndef QUIETBOOKGEN
170 static void
171 bkdisplay(char *s, int cnt, int moveno)
173 static short pnt;
174 struct leaf *node;
175 int r, c, l;
177 pnt = TrPnt[2];
178 printf("matches = %d\n", cnt);
179 printf("inout move is :%s: move number %d side %s\n",
180 s, moveno / 2 + 1, (moveno & 1) ? "white" : "black");
182 #ifndef SEMIQUIETBOOKGEN
183 printf("legal moves are \n");
185 while (pnt < TrPnt[3])
187 node = &Tree[pnt++];
189 if (is_promoted[board[node->f]] )
190 Balgbr(node->f | 0x80, node->t, (short) node->flags);
191 else
192 Balgbr(node->f, node->t, (short) node->flags);
194 printf("%s %s %s\n",
195 bmvstr[0], bmvstr[1], bmvstr[2]);
198 printf("\n current board is\n");
200 for (r = (NO_ROWS - 1); r >= 0; r--)
202 for (c = 0; c <= (NO_COLS - 1); c++)
204 char pc;
206 l = locn(r, c);
207 pc = (is_promoted[board[l]] ? '+' : ' ');
209 if (color[l] == neutral)
210 printf(" -");
211 else if (color[l] == black)
212 printf("%c%c", pc, qxx[board[l]]);
213 else
214 printf("%c%c", pc, pxx[board[l]]);
217 printf("\n");
220 printf("\n");
222 short color;
224 for (color = black; color <= white; color++)
226 short piece, c;
228 printf((color == black) ? "black " : "white ");
230 for (piece = pawn; piece <= king; piece++)
232 if ((c = Captured[color][piece]))
233 printf("%i%c ", c, pxx[piece]);
236 printf("\n");
239 #endif /* SEMIQUIETBOOKGEN */
241 #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.
251 static int
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 puts("Illegal move (in check): %s");
293 bkdisplay(s, cnt, moveno);
294 #endif
295 return false;
297 else
299 *mv = (xnode.f << 8) | xnode.t;
301 if (is_promoted[board[xnode.t]] )
302 Balgbr(xnode.f | 0x80, xnode.t, 0);
303 else
304 Balgbr(xnode.f, xnode.t, 0);
306 return true;
310 /* Illegal move */
311 #if !defined QUIETBOOKGEN
312 printf("Illegal move (no match): %s\n", s);
313 bkdisplay(s, cnt, moveno);
314 #endif
315 return false;
320 * RESET()
322 * Reset the board and other variables to start a new game.
326 static void
327 RESET(void)
329 short l;
331 flag.illegal = flag.mate = flag.quit
332 = flag.reverse = flag.bothsides = flag.onemove = flag.force
333 = false;
335 flag.post &= xboard; /* [HGM] xboard: do not clear in XBoard mode */
337 flag.material = flag.coords = flag.hash = flag.easy
338 = flag.beep = flag.rcptr
339 = true;
341 flag.stars = flag.shade = flag.back = flag.musttimeout = false;
342 flag.gamein = false;
343 GenCnt = 0;
344 GameCnt = 0;
345 CptrFlag[0] = TesujiFlag[0] = false;
346 opponent = black;
347 computer = white;
349 for (l = 0; l < NO_SQUARES; l++)
351 board[l] = Stboard[l];
352 color[l] = Stcolor[l];
353 Mvboard[l] = 0;
356 ClearCaptured();
357 InitializeStats();
358 hashbd = hashkey = 0;
362 static int
363 Vparse (FILE * fd, USHORT *mv, USHORT *flags, int moveno)
365 int c, i;
366 char s[255];
368 *flags = 0;
370 while (true)
372 while (((c = getc(fd)) == ' ')
373 || (c == '!') || (c == '/') || (c == '\n'));
375 if (c == '(')
377 /* amount of time spent for the last move */
378 while (((c = getc(fd)) != ')') && (c != EOF));
380 if (c == ')')
382 while (((c = getc(fd)) == ' ') || (c == '\n'));
386 if (c == '[')
388 /* comment for the actual game */
389 while (((c = getc(fd))) != ']' && (c != EOF));
391 if (c == ']')
393 while (((c = getc(fd))) == ' ' || (c == '\n'));
397 if (c == '\r')
398 continue;
400 if (c == '#')
402 /* comment */
405 c = getc(fd);
407 if (c == '\r')
408 continue;
409 /* goes to end of line */
411 if (c == '\n')
412 return 0;
414 if (c == EOF)
415 return -1;
417 while (true);
420 s[i = 0] = (char) c;
422 while ((c >= '0') && (c <= '9'))
424 c = getc(fd);
425 s[++i] = (char) c;
428 if (c == '.')
430 while (((c = getc(fd)) == ' ') || (c == '.') || (c == '\n'));
431 s[i = 0] = (char) c;
434 while (((c = getc(fd)) != '?') && (c != '!') && (c != ' ')
435 && (c != '(') && (c != '\n') && (c != '\t') && (c != EOF))
437 if (c == '\r')
438 continue;
440 if ((c != 'x') && (c != '-') && (c != ',')
441 && (c != ';') && (c != '='))
443 s[++i] = (char) c;
447 s[++i] = '\0';
449 if (c == '(')
451 while (((c = getc(fd)) != ')') && (c != EOF));
453 if (c == ')')
454 c = getc(fd);
457 if (c == EOF)
458 return -1;
460 if (s[0] == '#')
462 while ((c != '\n') && (c != EOF))
463 c = getc(fd);
465 if (c == EOF)
466 return -1;
467 else
468 return 0;
471 if (strcmp(s, "draw") == 0)
472 continue;
473 else if (strcmp(s, "1-0") == 0)
474 continue;
475 else if (strcmp(s, "0-1") == 0)
476 continue;
477 else if (strcmp(s, "Resigns") == 0)
478 continue;
479 else if (strcmp(s, "Resigns.") == 0)
480 continue;
481 else if (strcmp(s, "Sennichite") == 0)
482 continue;
483 else if (strcmp(s, "Sennichite.") == 0)
484 continue;
485 else if (strcmp(s, "Jishogi") == 0)
486 continue;
487 else if (strcmp(s, "Jishogi.") == 0)
488 continue;
490 bhashkey = hashkey;
491 bhashbd = hashbd;
493 i = BVerifyMove(s, mv, moveno);
495 if (c == '?')
497 /* Bad move, not for the program to play */
498 *flags |= BADMOVE; /* Flag it ! */
499 while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
501 #ifdef EASY_OPENINGS
502 else if (c == '~')
504 /* Do not use by computer */
505 *flags |= BADMOVE; /* Flag it ! */
507 while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
509 #endif
510 else if (c == '!')
512 /* Good move */
513 *flags |= GOODMOVE; /* Flag it ! */
515 while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
517 else if (c == '\r')
519 c = getc(fd);
522 if (c == '(' )
523 while (((c = getc(fd)) != ')') && (c != EOF));
525 if (!i)
527 /* flush to start of next */
528 while (((c = getc(fd)) != '#') && (c != EOF));
530 if (c == EOF)
532 return -1;
534 else
536 ungetc(c, fd);
537 return i;
541 return i;
546 static struct gdxadmin ADMIN;
547 struct gdxadmin B;
548 static struct gdxdata DATA;
550 /* lts(l) returns most significant 16 bits of l */
552 #if SIZEOF_LONG == 8 /* 64-bit long i.e. 8 bytes */
553 # define lts(x) (USHORT)(((x >> 48) & 0xfffe) | side)
554 #else
555 # if defined USE_LTSIMP
556 static USHORT ltsimp(long x)
558 USHORT n;
559 n = (((x >> 16) & 0xfffe));
560 return n;
562 # define lts(x) (USHORT)(ltsimp(x) | side)
563 # else
564 # define lts(x) (USHORT)(((x >> 16)&0xfffe) | side)
565 # endif
566 #endif
569 /* #define HashValue(l) lts(l) */
570 #define HashValue(l) (USHORT)(l & 0xffff)
572 static int gfd;
574 #define MAXOFFSET(B) ((B.booksize - 1) * sizeof_gdxdata + sizeof_gdxadmin)
576 static ULONG HashOffset(ULONG hashkey, struct gdxadmin *B)
578 return (hashkey % B->booksize) * sizeof_gdxdata + sizeof_gdxadmin;
582 static ULONG NextOffset(struct gdxadmin *B, ULONG offset)
584 offset += sizeof_gdxdata;
585 if (offset > B->maxoffset)
586 offset = sizeof_gdxadmin;
587 return offset;
591 static void WriteAdmin(void)
593 lseek(gfd, 0, SEEK_SET);
594 write(gfd, (char *)&ADMIN, sizeof_gdxadmin);
597 static void WriteData(ULONG offset, int *mustwrite)
599 if (!*mustwrite)
600 return;
602 lseek(gfd, offset, SEEK_SET);
603 write(gfd, (char *)&DATA, sizeof_gdxdata);
604 *mustwrite = false;
607 static int ReadAdmin(void)
609 lseek(gfd, 0, SEEK_SET);
610 return (sizeof_gdxadmin == read(gfd, (char *)&ADMIN, sizeof_gdxadmin));
613 static int ReadData(ULONG offset, struct gdxdata *DATA)
615 lseek(gfd, offset, SEEK_SET);
616 return (sizeof_gdxdata == read(gfd, (char *)DATA, sizeof_gdxdata));
621 * GetOpenings()
623 * CHECKME: is this still valid esp. wrt gnushogi.book?
625 * Read in the Opening Book file and parse the algebraic notation for a move
626 * into an unsigned integer format indicating the from and to square. Create
627 * a linked list of opening lines of play, with entry->next pointing to the
628 * next line and entry->move pointing to a chunk of memory containing the
629 * moves. More Opening lines of up to 100 half moves may be added to
630 * gnushogi.book. But now it's a hashed table by position which yields a move
631 * or moves for each position. It no longer knows about openings per se only
632 * positions and recommended moves in those positions.
636 void
637 GetOpenings(void)
639 ULONG currentoffset = 0;
640 short i;
641 int first;
642 unsigned short side;
643 short c;
644 USHORT mv, flags;
645 unsigned int x;
646 unsigned int games = 0;
647 LONG collisions = 0;
648 char msg[80];
650 FILE *fd;
652 if ((fd = fopen(bookfile, "r")) == NULL)
653 fd = fopen("gnushogi.tbk", "r");
655 if (fd != NULL)
657 /* yes add to book */
658 /* open book as writer */
659 gfd = open(binbookfile, O_RDONLY | O_BINARY);
661 if (gfd >= 0)
663 if (ReadAdmin())
665 B.bookcount = ADMIN.bookcount;
666 B.booksize = ADMIN.booksize;
667 B.maxoffset = ADMIN.maxoffset;
669 if (B.booksize && !(B.maxoffset == MAXOFFSET(B)))
671 printf("bad format %s\n", binbookfile);
672 exit(1);
675 else
677 printf("bad format %s\n", binbookfile);
678 exit(1);
680 close(gfd);
681 gfd = open(binbookfile, O_RDWR | O_BINARY);
684 else
686 gfd = open(binbookfile, O_RDWR | O_CREAT | O_BINARY, 0644);
688 ADMIN.bookcount = B.bookcount = 0;
689 ADMIN.booksize = B.booksize = booksize;
690 B.maxoffset = ADMIN.maxoffset = MAXOFFSET(B);
691 DATA.hashbd = 0;
692 DATA.hashkey = 0;
693 DATA.bmove = 0;
694 DATA.flags = 0;
695 DATA.hint = 0;
696 DATA.count = 0;
697 WriteAdmin();
698 printf("creating bookfile %s %ld %ld\n",
699 binbookfile, B.maxoffset, B.booksize);
701 for (x = 0; x < B.booksize; x++)
703 int mustwrite = true;
704 WriteData(sizeof_gdxadmin + x* sizeof_gdxdata, &mustwrite);
708 if (gfd >= 0)
710 int mustwrite = false;
711 /* setvbuf(fd, buffr, _IOFBF, 2048); */
712 side = black;
713 hashbd = hashkey = 0;
714 i = 0;
716 while ((c = Vparse(fd, &mv, &flags, i)) >= 0)
718 if (c == 1)
721 * If this is not the first move of an opening and
722 * if it's the first time we have seen it then
723 * save the next move as a hint.
725 i++;
727 if (i < bookmaxply + 2)
729 if (i > 1 && !(flags & BADMOVE))
730 DATA.hint = mv;
732 if (i < bookmaxply + 1)
735 * See if this position and move already
736 * exist from some other opening.
739 WriteData(currentoffset, &mustwrite);
740 currentoffset = HashOffset(bhashkey, &B);
741 first = true;
743 while (true)
745 if (!ReadData(currentoffset, &DATA))
746 break; /* corrupted binbook file */
748 if (DATA.bmove == 0)
749 break; /* free entry */
751 if (DATA.hashkey == HashValue(bhashkey)
752 && DATA.hashbd == bhashbd)
754 if (DATA.bmove == mv)
757 * Yes, so just bump count - count
758 * is used to choose the opening
759 * move in proportion to its
760 * presence in the book.
763 DATA.count++;
764 DATA.flags |= flags;
765 mustwrite = true;
766 break;
768 else
770 if (first)
771 collisions++;
773 if (DATA.flags & LASTMOVE)
775 DATA.flags &= (~LASTMOVE);
776 mustwrite = true;
777 WriteData(currentoffset, &mustwrite);
782 currentoffset = NextOffset(&B, currentoffset);
783 first = false;
787 * Doesn't exist so add it to the book.
790 if (!mustwrite)
792 B.bookcount++;
794 if ((B.bookcount % 1000) == 0)
796 /* CHECKME: may want to get rid of this,
797 * especially for xshogi. */
798 printf("%ld rec %d openings "
799 "processed\n",
800 B.bookcount, games);
803 /* initialize a record */
804 DATA.hashbd = bhashbd;
805 DATA.hashkey = HashValue(bhashkey);
806 DATA.bmove = mv;
807 DATA.flags = flags | LASTMOVE;
808 DATA.count = 1;
809 DATA.hint = 0;
810 mustwrite = true;
815 computer = opponent;
816 opponent = computer ^ 1;
818 side = side ^ 1;
820 else if (i > 0)
822 /* reset for next opening */
823 games++;
824 WriteData(currentoffset, &mustwrite);
825 RESET();
826 i = 0;
827 side = black;
831 WriteData(currentoffset, &mustwrite);
832 fclose(fd);
833 /* write admin rec with counts */
834 ADMIN.bookcount = B.bookcount;
835 WriteAdmin();
837 close(gfd);
841 if (binbookfile != NULL)
843 /* open book as reader */
844 gfd = open(binbookfile, O_RDONLY | O_BINARY);
846 if (gfd >= 0)
848 if (ReadAdmin() && (!ADMIN.booksize
849 || (ADMIN.maxoffset == MAXOFFSET(ADMIN))))
851 B.bookcount = ADMIN.bookcount;
852 B.booksize = ADMIN.booksize;
853 B.maxoffset = ADMIN.maxoffset;
855 else
857 printf("bad format %s\n", binbookfile);
858 exit(1);
862 else
864 B.bookcount = 0;
865 B.booksize = booksize;
869 sprintf(msg, "Book used %lu(%lu).", B.bookcount, B.booksize);
870 dsp->ShowMessage(msg);
873 /* Set everything back to start the game. */
874 Book = BOOKFAIL;
875 RESET();
877 /* Now get ready to play .*/
878 if (!B.bookcount)
880 dsp->ShowMessage("Can't find book.");
881 Book = 0;
887 * OpeningBook(hint)
889 * Go through each of the opening lines of play and check for a match with
890 * the current game listing. If a match occurs, generate a random
891 * number. If this number is the largest generated so far then the next
892 * move in this line becomes the current "candidate". After all lines are
893 * checked, the candidate move is put at the top of the Tree[] array and
894 * will be played by the program. Note that the program does not handle
895 * book transpositions.
899 OpeningBook(unsigned short *hint)
901 ULONG currentoffset;
902 unsigned short r, m;
903 int possibles = TrPnt[2] - TrPnt[1];
905 gsrand((unsigned int) time((long *) 0));
906 m = 0;
909 * Find all the moves for this position - count them and get their
910 * total count.
914 USHORT i, x;
915 USHORT rec = 0;
916 USHORT summ = 0;
917 USHORT h = 0, b = 0;
918 struct gdxdata OBB[128];
920 if (B.bookcount == 0)
922 Book--;
923 return false;
926 x = 0;
927 currentoffset = HashOffset(hashkey, &B);
928 #ifdef BOOKTEST
929 printf("looking for book move, bhashbd = 0x%lx bhashkey = 0x%x\n",
930 (ULONG)hashbd, HashValue(hashkey));
931 #endif
932 while (true)
934 if (!ReadData(currentoffset, &OBB[x]))
935 break;
937 if (OBB[x].bmove == 0)
938 break;
940 #ifdef BOOKTEST
941 printf("compare with bhashbd = 0x%lx bhashkey = 0x%x\n",
942 OBB[x].hashbd, OBB[x].hashkey);
943 #endif
944 if ((OBB[x].hashkey == HashValue(hashkey))
945 && (OBB[x].hashbd == (ULONG)hashbd))
947 x++;
949 if (OBB[x-1].flags & LASTMOVE)
950 break;
953 currentoffset = NextOffset(&B, currentoffset);
956 #ifdef BOOKTEST
957 printf("%d book move(s) found.\n", x);
958 #endif
960 if (x == 0)
962 Book--;
963 return false;
966 for (i = 0; i < x; i++)
968 if (OBB[i].flags & BADMOVE)
970 m = OBB[i].bmove;
972 /* Is the move in the MoveList? */
973 for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
975 if (((Tree[b].f << 8) | Tree[b].t) == m)
977 if (--possibles)
978 Tree[b].score = DONTUSE;
979 break;
983 else
985 #if defined BOOKTEST
986 char s[20];
987 movealgbr(m = OBB[i].bmove, s);
988 printf("finding book move: %s\n", s);
989 #endif
990 summ += OBB[i].count;
994 if (summ == 0)
996 Book--;
997 return false;
1000 r = (urand() % summ);
1002 for (i = 0; i < x; i++)
1004 if (!(OBB[i].flags & BADMOVE))
1006 if (r < OBB[i].count)
1008 rec = i;
1009 break;
1011 else
1013 r -= OBB[i].count;
1018 h = OBB[rec].hint;
1019 m = OBB[rec].bmove;
1021 /* Make sure the move is in the MoveList. */
1022 for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
1024 if (((Tree[b].f << 8) | Tree[b].t) == m)
1026 Tree[b].flags |= book;
1027 Tree[b].score = 0;
1028 break;
1032 /* Make sure it's the best. */
1034 pick(TrPnt[1], TrPnt[2] - 1);
1036 if (Tree[TrPnt[1]].score)
1038 /* no! */
1039 Book--;
1040 return false;
1043 /* Ok, pick up the hint and go. */
1044 *hint = h;
1045 return true;
1048 Book--;
1049 return false;