Curses: fix inverted column numbers display for minishogi.
[gnushogi.git] / gnushogi / book.c
blob009e409493e9197af19f90be5b33667455fd73c0
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, flag)
73 * Generate move strings in different formats.
76 void
77 Balgbr(short f, short t, short flag)
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 flag = (dropmask | piece);
98 if ((t & 0x80) != 0)
100 flag |= 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 ((flag & dropmask) != 0)
116 /* bmvstr[0]: P*3c bmvstr[1]: P'3c */
117 short piece = flag & pmask;
118 bmvstr[0][0] = pxx[piece];
119 bmvstr[0][1] = '*';
120 bmvstr[0][2] = cxx[column(t)];
121 bmvstr[0][3] = rxx[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] = cxx[column(f)];
134 bmvstr[0][1] = rxx[row(f)];
135 bmvstr[0][2] = cxx[column(t)];
136 bmvstr[0][3] = rxx[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 (flag & 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';
171 #ifndef QUIETBOOKGEN
172 void
173 bkdisplay(char *s, int cnt, int moveno)
175 static short pnt;
176 struct leaf *node;
177 int r, c, l;
179 pnt = TrPnt[2];
180 printf("matches = %d\n", cnt);
181 printf("inout move is :%s: move number %d side %s\n",
182 s, moveno / 2 + 1, (moveno & 1) ? "white" : "black");
184 #ifndef SEMIQUIETBOOKGEN
185 printf("legal moves are \n");
187 while (pnt < TrPnt[3])
189 node = &Tree[pnt++];
191 if (is_promoted[board[node->f]] )
192 Balgbr(node->f | 0x80, node->t, (short) node->flags);
193 else
194 Balgbr(node->f, node->t, (short) node->flags);
196 printf("%s %s %s\n",
197 bmvstr[0], bmvstr[1], bmvstr[2]);
200 printf("\n current board is\n");
202 for (r = (NO_ROWS - 1); r >= 0; r--)
204 for (c = 0; c <= (NO_COLS - 1); c++)
206 char pc;
208 l = locn(r, c);
209 pc = (is_promoted[board[l]] ? '+' : ' ');
211 if (color[l] == neutral)
212 printf(" -");
213 else if (color[l] == black)
214 printf("%c%c", pc, qxx[board[l]]);
215 else
216 printf("%c%c", pc, pxx[board[l]]);
219 printf("\n");
222 printf("\n");
224 short color;
226 for (color = black; color <= white; color++)
228 short piece, c;
230 printf((color == black) ? "black " : "white ");
232 for (piece = pawn; piece <= king; piece++)
234 if ((c = Captured[color][piece]))
235 printf("%i%c ", c, pxx[piece]);
238 printf("\n");
241 #endif /* SEMIQUIETBOOKGEN */
244 #endif /* QUIETBOOKGEN */
249 * BVerifyMove(s, mv, moveno)
251 * Compare the string 's' to the list of legal moves available for the
252 * opponent. If a match is found, make the move on the board.
256 BVerifyMove(char *s, unsigned short *mv, int moveno)
258 static short pnt, tempb, tempc, tempsf, tempst, cnt;
259 static struct leaf xnode;
260 struct leaf *node;
262 *mv = 0;
263 cnt = 0;
264 MoveList(opponent, 2, -2, true);
265 pnt = TrPnt[2];
267 while (pnt < TrPnt[3])
269 node = &Tree[pnt++];
271 if (is_promoted[board[node->f]] )
272 Balgbr(node->f | 0x80, node->t, (short) node->flags);
273 else
274 Balgbr(node->f, node->t, (short) node->flags);
276 if (strcmp(s, bmvstr[0]) == 0 || strcmp(s, bmvstr[1]) == 0 ||
277 strcmp(s, bmvstr[2]) == 0)
279 cnt++;
280 xnode = *node;
284 if (cnt == 1)
286 short blockable;
288 MakeMove(opponent, &xnode, &tempb,
289 &tempc, &tempsf, &tempst, &INCscore);
291 if (SqAttacked(PieceList[opponent][0], computer, &blockable))
293 UnmakeMove(opponent, &xnode, &tempb, &tempc, &tempsf, &tempst);
294 /* Illegal move in check */
295 #if !defined QUIETBOOKGEN
296 puts("Illegal move (in check) %s");
297 bkdisplay(s, cnt, moveno);
298 #endif
299 return false;
301 else
303 *mv = (xnode.f << 8) | xnode.t;
305 if (is_promoted[board[xnode.t]] )
306 Balgbr(xnode.f | 0x80, xnode.t, false);
307 else
308 Balgbr(xnode.f, xnode.t, false);
310 return true;
314 /* Illegal move */
315 #if !defined QUIETBOOKGEN
316 printf("Illegal move (no match) %s\n", s);
317 bkdisplay(s, cnt, moveno);
318 #endif
319 return false;
326 * RESET()
328 * Reset the board and other variables to start a new game.
332 void
333 RESET(void)
335 short l;
337 flag.illegal = flag.mate = flag.post = flag.quit
338 = flag.reverse = flag.bothsides = flag.onemove = flag.force
339 = false;
341 flag.material = flag.coords = flag.hash = flag.easy
342 = flag.beep = flag.rcptr
343 = true;
345 flag.stars = flag.shade = flag.back = flag.musttimeout = false;
346 flag.gamein = false;
347 GenCnt = 0;
348 GameCnt = 0;
349 CptrFlag[0] = TesujiFlag[0] = false;
350 opponent = black;
351 computer = white;
353 for (l = 0; l < NO_SQUARES; l++)
355 board[l] = Stboard[l];
356 color[l] = Stcolor[l];
357 Mvboard[l] = 0;
360 ClearCaptured();
361 InitializeStats();
362 hashbd = hashkey = 0;
367 static
369 Vparse (FILE * fd, USHORT *mv, USHORT *flags, USHORT side, int moveno)
371 int c, i;
372 char s[255];
374 *flags = 0;
376 while (true)
378 while (((c = getc(fd)) == ' ')
379 || (c == '!') || (c == '/') || (c == '\n'));
381 if (c == '(')
383 /* amount of time spent for the last move */
384 while (((c = getc(fd)) != ')') && (c != EOF));
386 if (c == ')')
388 while (((c = getc(fd)) == ' ') || (c == '\n'));
392 if (c == '[')
394 /* comment for the actual game */
395 while (((c = getc(fd))) != ']' && (c != EOF));
397 if (c == ']')
399 while (((c = getc(fd))) == ' ' || (c == '\n'));
403 if (c == '\r')
404 continue;
406 if (c == '#')
408 /* comment */
411 c = getc(fd);
413 if (c == '\r')
414 continue;
415 /* goes to end of line */
417 if (c == '\n')
418 return 0;
420 if (c == EOF)
421 return -1;
423 while (true);
426 s[i = 0] = (char) c;
428 while ((c >= '0') && (c <= '9'))
430 c = getc(fd);
431 s[++i] = (char) c;
434 if (c == '.')
436 while (((c = getc(fd)) == ' ') || (c == '.') || (c == '\n'));
437 s[i = 0] = (char) c;
440 while (((c = getc(fd)) != '?') && (c != '!') && (c != ' ')
441 && (c != '(') && (c != '\n') && (c != '\t') && (c != EOF))
443 if (c == '\r')
444 continue;
446 if ((c != 'x') && (c != '-') && (c != ',')
447 && (c != ';') && (c != '='))
449 s[++i] = (char) c;
453 s[++i] = '\0';
455 if (c == '(')
457 while (((c = getc(fd)) != ')') && (c != EOF));
459 if (c == ')')
460 c = getc(fd);
463 if (c == EOF)
464 return -1;
466 if (s[0] == '#')
468 while ((c != '\n') && (c != EOF))
469 c = getc(fd);
471 if (c == EOF)
472 return -1;
473 else
474 return 0;
477 if (strcmp(s, "draw") == 0)
478 continue;
479 else if (strcmp(s, "1-0") == 0)
480 continue;
481 else if (strcmp(s, "0-1") == 0)
482 continue;
483 else if (strcmp(s, "Resigns") == 0)
484 continue;
485 else if (strcmp(s, "Resigns.") == 0)
486 continue;
487 else if (strcmp(s, "Sennichite") == 0)
488 continue;
489 else if (strcmp(s, "Sennichite.") == 0)
490 continue;
491 else if (strcmp(s, "Jishogi") == 0)
492 continue;
493 else if (strcmp(s, "Jishogi.") == 0)
494 continue;
496 bhashkey = hashkey;
497 bhashbd = hashbd;
499 i = BVerifyMove(s, mv, moveno);
501 if (c == '?')
503 /* Bad move, not for the program to play */
504 *flags |= BADMOVE; /* Flag it ! */
505 while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
507 #ifdef EASY_OPENINGS
508 else if (c == '~')
510 /* Do not use by computer */
511 *flags |= BADMOVE; /* Flag it ! */
513 while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
515 #endif
516 else if (c == '!')
518 /* Good move */
519 *flags |= GOODMOVE; /* Flag it ! */
521 while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
523 else if (c == '\r')
525 c = getc(fd);
528 if (c == '(' )
529 while (((c = getc(fd)) != ')') && (c != EOF));
531 if (!i)
533 /* flush to start of next */
534 while (((c = getc(fd)) != '#') && (c != EOF));
536 if (c == EOF)
538 return -1;
540 else
542 ungetc(c, fd);
543 return i;
547 return i;
552 static struct gdxadmin ADMIN;
553 struct gdxadmin B;
554 static struct gdxdata DATA;
556 /* lts(l) returns most significant 16 bits of l */
558 #if SIZEOF_LONG == 8 /* 64-bit long i.e. 8 bytes */
559 # define lts(x) (USHORT)(((x >> 48) & 0xfffe) | side)
560 #else
561 # if defined USE_LTSIMP
562 static USHORT ltsimp(long x)
564 USHORT n;
565 n = (((x >> 16) & 0xfffe));
566 return n;
568 # define lts(x) (USHORT)(ltsimp(x) | side)
569 # else
570 # define lts(x) (USHORT)(((x >> 16)&0xfffe) | side)
571 # endif
572 #endif
575 /* #define HashValue(l) lts(l) */
576 #define HashValue(l) (USHORT)(l & 0xffff)
579 static int gfd;
580 static ULONG currentoffset;
583 #define MAXOFFSET(B) ((B.booksize - 1) * sizeof_gdxdata + sizeof_gdxadmin)
585 #define HashOffset(hashkey, B) \
587 currentoffset = ((ULONG)hashkey % B.booksize) \
588 * sizeof_gdxdata + sizeof_gdxadmin; \
592 #define NextOffset(B) \
594 currentoffset += sizeof_gdxdata; \
595 if (currentoffset > B.maxoffset) \
596 currentoffset = sizeof_gdxadmin; \
600 #define WriteAdmin() \
602 lseek(gfd, 0, 0); \
603 write(gfd, (char *)&ADMIN, sizeof_gdxadmin); \
606 #define WriteData() \
608 if (mustwrite ) \
610 lseek(gfd, currentoffset, 0); \
611 write(gfd, (char *)&DATA, sizeof_gdxdata); \
612 mustwrite = false; \
616 static int ReadAdmin(void)
618 lseek(gfd, 0, 0);
619 return (sizeof_gdxadmin == read(gfd, (char *)&ADMIN, sizeof_gdxadmin));
622 static int ReadData(struct gdxdata *DATA)
624 lseek(gfd, currentoffset, 0);
625 return (sizeof_gdxdata == read(gfd, (char *)DATA, sizeof_gdxdata));
630 * GetOpenings()
632 * CHECKME: is this still valid esp. wrt gnushogi.book?
634 * Read in the Opening Book file and parse the algebraic notation for a move
635 * into an unsigned integer format indicating the from and to square. Create
636 * a linked list of opening lines of play, with entry->next pointing to the
637 * next line and entry->move pointing to a chunk of memory containing the
638 * moves. More Opening lines of up to 100 half moves may be added to
639 * gnushogi.book. But now it's a hashed table by position which yields a move
640 * or moves for each position. It no longer knows about openings per se only
641 * positions and recommended moves in those positions.
645 void
646 GetOpenings(void)
648 short i;
649 int mustwrite = false, first;
650 unsigned short xside, side;
651 short c;
652 USHORT mv, flags;
653 unsigned int x;
654 unsigned int games = 0;
655 LONG collisions = 0;
656 char msg[80];
658 FILE *fd;
660 if ((fd = fopen(bookfile, "r")) == NULL)
661 fd = fopen("gnushogi.tbk", "r");
663 if (fd != NULL)
665 /* yes add to book */
666 /* open book as writer */
667 gfd = open(binbookfile, O_RDONLY | O_BINARY);
669 if (gfd >= 0)
671 if (ReadAdmin())
673 B.bookcount = ADMIN.bookcount;
674 B.booksize = ADMIN.booksize;
675 B.maxoffset = ADMIN.maxoffset;
677 if (B.booksize && !(B.maxoffset == MAXOFFSET(B)))
679 printf("bad format %s\n", binbookfile);
680 exit(1);
683 else
685 printf("bad format %s\n", binbookfile);
686 exit(1);
688 close(gfd);
689 gfd = open(binbookfile, O_RDWR | O_BINARY);
692 else
694 gfd = open(binbookfile, O_RDWR | O_CREAT | O_BINARY, 0644);
696 ADMIN.bookcount = B.bookcount = 0;
697 ADMIN.booksize = B.booksize = booksize;
698 B.maxoffset = ADMIN.maxoffset = MAXOFFSET(B);
699 DATA.hashbd = 0;
700 DATA.hashkey = 0;
701 DATA.bmove = 0;
702 DATA.flags = 0;
703 DATA.hint = 0;
704 DATA.count = 0;
705 write(gfd, (char *)&ADMIN, sizeof_gdxadmin);
706 printf("creating bookfile %s %ld %ld\n",
707 binbookfile, B.maxoffset, B.booksize);
709 for (x = 0; x < B.booksize; x++)
711 write(gfd, (char *)&DATA, sizeof_gdxdata);
715 if (gfd >= 0)
717 /* setvbuf(fd, buffr, _IOFBF, 2048); */
718 side = black;
719 xside = white;
720 hashbd = hashkey = 0;
721 i = 0;
723 while ((c = Vparse(fd, &mv, &flags, side, i)) >= 0)
725 if (c == 1)
728 * If this is not the first move of an opening and
729 * if it's the first time we have seen it then
730 * save the next move as a hint.
732 i++;
734 if (i < bookmaxply + 2)
736 if (i > 1 && !(flags & BADMOVE))
737 DATA.hint = mv;
739 if (i < bookmaxply + 1)
742 * See if this position and move already
743 * exist from some other opening.
746 WriteData();
747 HashOffset(bhashkey, B);
748 first = true;
750 while (true)
752 if (!ReadData(&DATA))
753 break; /* corrupted binbook file */
755 if (DATA.bmove == 0)
756 break; /* free entry */
758 if (DATA.hashkey == HashValue(bhashkey)
759 && DATA.hashbd == bhashbd)
761 if (DATA.bmove == mv)
764 * Yes, so just bump count - count
765 * is used to choose the opening
766 * move in proportion to its
767 * presence in the book.
770 DATA.count++;
771 DATA.flags |= flags;
772 mustwrite = true;
773 break;
775 else
777 if (first)
778 collisions++;
780 if (DATA.flags & LASTMOVE)
782 DATA.flags &= (~LASTMOVE);
783 mustwrite = true;
784 WriteData();
789 NextOffset(B);
790 first = false;
794 * Doesn't exist so add it to the book.
797 if (!mustwrite)
799 B.bookcount++;
801 if ((B.bookcount % 1000) == 0)
803 /* CHECKME: may want to get rid of this,
804 * especially for xshogi. */
805 printf("%ld rec %d openings "
806 "processed\n",
807 B.bookcount, games);
810 /* initialize a record */
811 DATA.hashbd = bhashbd;
812 DATA.hashkey = HashValue(bhashkey);
813 DATA.bmove = mv;
814 DATA.flags = flags | LASTMOVE;
815 DATA.count = 1;
816 DATA.hint = 0;
817 mustwrite = true;
822 computer = opponent;
823 opponent = computer ^ 1;
825 xside = side;
826 side = side ^ 1;
828 else if (i > 0)
830 /* reset for next opening */
831 games++;
832 WriteData();
833 RESET();
834 i = 0;
835 side = black;
836 xside = white;
841 WriteData();
842 fclose(fd);
843 /* write admin rec with counts */
844 ADMIN.bookcount = B.bookcount;
845 WriteAdmin();
847 close(gfd);
851 if (binbookfile != NULL)
853 /* open book as reader */
854 gfd = open(binbookfile, O_RDONLY | O_BINARY);
856 if (gfd >= 0)
858 if (ReadAdmin() && (!ADMIN.booksize
859 || (ADMIN.maxoffset == MAXOFFSET(ADMIN))))
861 B.bookcount = ADMIN.bookcount;
862 B.booksize = ADMIN.booksize;
863 B.maxoffset = ADMIN.maxoffset;
865 else
867 printf("bad format %s\n", binbookfile);
868 exit(1);
872 else
874 B.bookcount = 0;
875 B.booksize = booksize;
879 sprintf(msg, "Book used %d(%d).", 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 ShowMessage("Can't find book.");
891 Book = 0;
898 * OpeningBook(hint, side)
900 * Go through each of the opening lines of play and check for a match with
901 * the current game listing. If a match occurs, generate a random
902 * number. If this number is the largest generated so far then the next
903 * move in this line becomes the current "candidate". After all lines are
904 * checked, the candidate move is put at the top of the Tree[] array and
905 * will be played by the program. Note that the program does not handle
906 * book transpositions.
910 OpeningBook(unsigned short *hint, short side)
912 unsigned short r, m;
913 int possibles = TrPnt[2] - TrPnt[1];
915 gsrand((unsigned int) time((long *) 0));
916 m = 0;
919 * Find all the moves for this position - count them and get their
920 * total count.
924 USHORT i, x;
925 USHORT rec = 0;
926 USHORT summ = 0;
927 USHORT h = 0, b = 0;
928 struct gdxdata OBB[128];
930 if (B.bookcount == 0)
932 Book--;
933 return false;
936 x = 0;
937 HashOffset(hashkey, B);
938 #ifdef BOOKTEST
939 printf("looking for book move, bhashbd = 0x%lx bhashkey = 0x%x\n",
940 (ULONG)hashbd, HashValue(hashkey));
941 #endif
942 while (true)
944 if (!ReadData(&OBB[x]))
945 break;
947 if (OBB[x].bmove == 0)
948 break;
950 #ifdef BOOKTEST
951 printf("compare with bhashbd = 0x%lx bhashkey = 0x%x\n",
952 OBB[x].hashbd, OBB[x].hashkey);
953 #endif
954 if ((OBB[x].hashkey == HashValue(hashkey))
955 && (OBB[x].hashbd == (ULONG)hashbd))
957 x++;
959 if (OBB[x-1].flags & LASTMOVE)
960 break;
963 NextOffset(B);
966 #ifdef BOOKTEST
967 printf("%d book move(s) found.\n", x);
968 #endif
970 if (x == 0)
972 Book--;
973 return false;
976 for (i = 0; i < x; i++)
978 if (OBB[i].flags & BADMOVE)
980 m = OBB[i].bmove;
982 /* Is the move in the MoveList? */
983 for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
985 if (((Tree[b].f << 8) | Tree[b].t) == m)
987 if (--possibles)
988 Tree[b].score = DONTUSE;
989 break;
993 else
995 #if defined BOOKTEST
996 char s[20];
997 movealgbr(m = OBB[i].bmove, s);
998 printf("finding book move: %s\n", s);
999 #endif
1000 summ += OBB[i].count;
1004 if (summ == 0)
1006 Book--;
1007 return false;
1010 r = (urand() % summ);
1012 for (i = 0; i < x; i++)
1014 if (!(OBB[i].flags & BADMOVE))
1016 if (r < OBB[i].count)
1018 rec = i;
1019 break;
1021 else
1023 r -= OBB[i].count;
1028 h = OBB[rec].hint;
1029 m = OBB[rec].bmove;
1031 /* Make sure the move is in the MoveList. */
1032 for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
1034 if (((Tree[b].f << 8) | Tree[b].t) == m)
1036 Tree[b].flags |= book;
1037 Tree[b].score = 0;
1038 break;
1042 /* Make sure it's the best. */
1044 pick(TrPnt[1], TrPnt[2] - 1);
1046 if (Tree[TrPnt[1]].score)
1048 /* no! */
1049 Book--;
1050 return false;
1053 /* Ok, pick up the hint and go. */
1054 *hint = h;
1055 return true;
1058 Book--;
1059 return false;