2 * book.c - C source for GNU SHOGI
4 * Copyright (c) 1993, 1994, 1995 Matthias Mutz
6 * GNU SHOGI is based on GNU CHESS
8 * Copyright (c) 1988,1989,1990 John Stanback Copyright (c) 1992 Free Software
11 * This file is part of GNU SHOGI.
13 * GNU Shogi is free software; you can redistribute it and/or modify it under
14 * the terms of the GNU General Public License as published by the Free
15 * Software Foundation; either version 1, or (at your option) any later
18 * GNU Shogi is distributed in the hope that it will be useful, but WITHOUT ANY
19 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
23 * You should have received a copy of the GNU General Public License along with
24 * GNU Shogi; see the file COPYING. If not, write to the Free Software
25 * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
32 #if !defined MSDOS && !defined THINK_C
38 /* #define BOOKTEST */
46 unsigned booksize = BOOKSIZE;
47 unsigned short bookmaxply = BOOKMAXPLY;
48 unsigned bookcount = 0;
51 char *bookfile = BOOK;
53 char *bookfile = NULL;
56 char *binbookfile = BINBOOK;
58 char *binbookfile = NULL;
63 static char bmvstr[3][7];
66 static ULONG bhashkey;
70 Balgbr (short int f, short int t, short int flag)
74 * Generate move strings in different formats.
78 short promoted = false;
88 piece = f - NO_SQUARES;
89 if ( f > (NO_SQUARES+NO_PIECES) )
91 flag = (dropmask | piece);
93 if ( (t & 0x80) != 0 )
98 if ( f == t && (f != 0 || t != 0) )
102 sprintf(buffer,"error in algbr: FROM=TO=%d, flag=0x%4x\n",t,flag);
105 bmvstr[0][0] = bmvstr[1][0] = bmvstr[2][0] = '\0';
108 if ( (flag & dropmask) != 0 )
110 /* bmvstr[0]: P*3c bmvstr[1]: P'3c */
111 short piece = flag & pmask;
112 bmvstr[0][0] = pxx[piece];
114 bmvstr[0][2] = cxx[column (t)];
115 bmvstr[0][3] = rxx[row (t)];
116 bmvstr[0][4] = bmvstr[2][0] = '\0';
117 strcpy (bmvstr[1], bmvstr[0]);
121 if (f != 0 || t != 0)
123 /* algebraic notation */
124 /* bmvstr[0]: 7g7f bmvstr[1]: (+)P7g7f(+) bmvstr[2]: (+)P7f(+) */
125 bmvstr[0][0] = cxx[column (f)];
126 bmvstr[0][1] = rxx[row (f)];
127 bmvstr[0][2] = cxx[column (t)];
128 bmvstr[0][3] = rxx[row (t)];
132 bmvstr[1][0] = bmvstr[2][0] = '+';
133 bmvstr[1][1] = bmvstr[2][1] = pxx[board[f]];
134 strcpy(&bmvstr[1][2],&bmvstr[0][0]);
135 strcpy(&bmvstr[2][2],&bmvstr[0][2]);
139 bmvstr[1][0] = bmvstr[2][0] = pxx[board[f]];
140 strcpy(&bmvstr[1][1],&bmvstr[0][0]);
141 strcpy(&bmvstr[2][1],&bmvstr[0][2]);
145 strcat(bmvstr[0], "+");
146 strcat(bmvstr[1], "+");
147 strcat(bmvstr[2], "+");
151 bmvstr[0][0] = bmvstr[1][0] = bmvstr[2][0] = '\0';
159 bkdisplay (s, cnt, moveno)
165 struct leaf far *node;
169 printf ("matches = %d\n", cnt);
170 printf ("inout move is :%s: move number %d side %s\n",
171 s, moveno / 2 + 1, (moveno & 1) ? "white" : "black");
172 #ifndef SEMIQUIETBOOKGEN
173 printf ("legal moves are \n");
174 while (pnt < TrPnt[3])
177 if ( is_promoted[board[node->f]] )
178 Balgbr (node->f | 0x80, node->t, (short) node->flags);
180 Balgbr (node->f, node->t, (short) node->flags);
181 printf ("%s %s %s\n",
182 bmvstr[0], bmvstr[1], bmvstr[2]);
184 printf ("\n current board is\n");
185 for (r = (NO_ROWS-1); r >= 0; r--)
187 for (c = 0; c <= (NO_COLS-1); c++)
190 pc = (is_promoted[board[l]] ? '+' : ' ');
191 if (color[l] == neutral)
193 else if (color[l] == black)
194 printf ("%c%c", pc, qxx[board[l]]);
196 printf ("%c%c", pc, pxx[board[l]]);
203 for (color = black; color <= white; color++)
205 printf((color==black)?"black ":"white ");
206 for (piece = pawn; piece <= king; piece++)
207 if (c = Captured[color][piece])
208 printf("%i%c ",c,pxx[piece]);
212 #endif /* SEMIQUIETBOOKGEN */
215 #endif /* QUIETBOOKGEN */
218 BVerifyMove (char *s, short unsigned int *mv, int moveno)
221 * Compare the string 's' to the list of legal moves available for the
222 * opponent. If a match is found, make the move on the board.
226 static short pnt, tempb, tempc, tempsf, tempst, cnt;
227 static struct leaf xnode;
228 struct leaf far *node;
232 MoveList (opponent, 2, -2, true);
234 while (pnt < TrPnt[3])
237 if ( is_promoted[board[node->f]] )
238 Balgbr (node->f | 0x80, node->t, (short) node->flags);
240 Balgbr (node->f, node->t, (short) node->flags);
241 if (strcmp (s, bmvstr[0]) == 0 || strcmp (s, bmvstr[1]) == 0 ||
242 strcmp (s, bmvstr[2]) == 0)
250 MakeMove (opponent, &xnode, &tempb, &tempc, &tempsf, &tempst, &INCscore);
251 if (SqAtakd (PieceList[opponent][0], computer, &blockable))
253 UnmakeMove (opponent, &xnode, &tempb, &tempc, &tempsf, &tempst);
254 /* Illegal move in check */
255 #if !defined QUIETBOOKGEN
257 printf ("Illegal move (in check)");
262 bkdisplay (s, cnt, moveno);
268 *mv = (xnode.f << 8) | xnode.t;
269 if ( is_promoted[board[xnode.t]] )
270 Balgbr (xnode.f | 0x80, xnode.t, false);
272 Balgbr (xnode.f, xnode.t, false);
277 #if !defined QUIETBOOKGEN
279 printf ("Illegal move (no match) %s\n", s);
283 bkdisplay (s, cnt, moveno);
292 * Reset the board and other variables to start a new game.
298 flag.illegal = flag.mate = flag.post = flag.quit = flag.reverse = flag.bothsides = flag.onemove = flag.force = false;
299 flag.material = flag.coords = flag.hash = flag.easy = flag.beep = flag.rcptr = true;
300 flag.stars = flag.shade = flag.back = flag.musttimeout = false;
304 CptrFlag[0] = TesujiFlag[0] = false;
307 for (l = 0; l < NO_SQUARES; l++)
309 board[l] = Stboard[l];
310 color[l] = Stcolor[l];
315 hashbd = hashkey = 0;
320 Vparse (FILE * fd, USHORT *mv, USHORT *flags, USHORT side, int moveno)
330 while ((c = getc (fd)) == ' ' || c == '!' || c == '/' || c == '\n');
333 { /* amount of time spent for the last move */
334 while ((c = getc(fd)) != ')' && c != EOF);
336 while ((c = getc (fd)) == ' ' || c == '\n');
340 { /* comment for the actual game */
341 while ( (c = getc(fd)) != ']' && c != EOF );
343 while ((c = getc (fd)) == ' ' || c == '\n');
356 /* goes to end of line */
369 while ( c >= '0' && c <= '9' )
377 while ((c = getc (fd)) == ' ' || c == '.' || c == '\n');
381 while ((c = getc (fd)) != '?' && c != '!' && c != ' ' && c != '(' && c != '\n' && c != '\t' && c != EOF)
385 if (c != 'x' && c != '-' && c != ',' && c != ';' && c != '=')
392 while ((c = getc(fd)) != ')' && c != EOF);
402 while (c != '\n' && c != EOF)
410 if (strcmp (s, "draw") == 0)
412 else if (strcmp (s, "1-0") == 0)
414 else if (strcmp (s, "0-1") == 0)
416 else if (strcmp (s, "Resigns") == 0)
418 else if (strcmp (s, "Resigns.") == 0)
420 else if (strcmp (s, "Sennichite") == 0)
422 else if (strcmp (s, "Sennichite.") == 0)
424 else if (strcmp (s, "Jishogi") == 0)
426 else if (strcmp (s, "Jishogi.") == 0)
432 i = BVerifyMove (s, mv, moveno);
435 { /* Bad move, not for the program to play */
436 *flags |= BADMOVE; /* Flag it ! */
437 while ((c = getc (fd)) == '?' || c == '!' || c == '/');
441 { /* Do not use by computer */
442 *flags |= BADMOVE; /* Flag it ! */
443 while ((c = getc (fd)) == '?' || c == '!' || c == '/');
448 *flags |= GOODMOVE; /* Flag it ! */
449 while ((c = getc (fd)) == '?' || c == '!' || c == '/');
455 while ((c = getc(fd)) != ')' && c != EOF);
459 /* flush to start of next */
460 while ((c = getc (fd)) != '#' && c != EOF);
475 static struct gdxadmin ADMIN;
478 static struct gdxdata DATA;
482 /* lts(l) returns most significant 16 bits of l */
485 #define lts(x) (USHORT)(((x>>48)&0xfffe)|side)
487 #if defined THINK_C || defined USE_LTSIMP
488 static USHORT ltsimp (long x)
490 n = (((x>>16)&0xfffe));
492 printf("x=0x%lx lts(x)=0x%x\n",x,n);
496 #define lts(x) (USHORT)(ltsimp(x) | side)
498 #define lts(x) (USHORT)(((x>>16)&0xfffe) | side)
503 /* #define HashValue(l) lts(l) */
504 #define HashValue(l) (USHORT)(l & 0xffff)
510 static ULONG currentoffset;
513 #define MAXOFFSET(B) ((B.booksize-1)*sizeof_gdxdata + sizeof_gdxadmin)
515 #define HashOffset(hashkey,B) { \
516 currentoffset = ((ULONG)hashkey % B.booksize)*sizeof_gdxdata + sizeof_gdxadmin; \
519 #define NextOffset(B) { \
520 currentoffset += sizeof_gdxdata; \
521 if (currentoffset > B.maxoffset) \
522 currentoffset = sizeof_gdxadmin; \
528 #define WriteAdmin() { \
530 write (gfd, (char *)&ADMIN, sizeof_gdxadmin); \
533 #define WriteData() { \
535 lseek (gfd, currentoffset, 0); \
536 write (gfd, (char *)&DATA, sizeof_gdxdata); \
541 static int ReadAdmin(void) {
543 return (sizeof_gdxadmin == read (gfd, (char *)&ADMIN, sizeof_gdxadmin));
546 static int ReadData(struct gdxdata *DATA) {
547 lseek (gfd, currentoffset, 0);
548 return (sizeof_gdxdata == read (gfd, (char *)DATA, sizeof_gdxdata));
556 * Read in the Opening Book file and parse the algebraic notation for a move
557 * into an unsigned integer format indicating the from and to square. Create
558 * a linked list of opening lines of play, with entry->next pointing to the
559 * next line and entry->move pointing to a chunk of memory containing the
560 * moves. More Opening lines of up to 100 half moves may be added to
561 gnuchess.book. But now its a hashed table by position which yields a move
562 * or moves for each position. It no longer knows about openings per say only
563 * positions and recommended moves in those positions.
569 int mustwrite = false, first;
570 unsigned short xside, side;
572 USHORT mv, flags; unsigned int x;
573 unsigned int games = 0;
578 if ((fd = fopen (bookfile, "r")) == NULL)
579 fd = fopen ("gnushogi.tbk", "r");
582 /* yes add to book */
583 /* open book as writer */
584 gfd = open (binbookfile, O_RDONLY | O_BINARY);
589 B.bookcount = ADMIN.bookcount;
590 B.booksize = ADMIN.booksize;
591 B.maxoffset = ADMIN.maxoffset;
592 if (B.booksize && !(B.maxoffset == MAXOFFSET(B)))
594 printf ("bad format %s\n", binbookfile);
600 printf ("bad format %s\n", binbookfile);
604 gfd = open (binbookfile, O_RDWR | O_BINARY);
609 #if defined THINK_C || defined MSDOS
610 gfd = open (binbookfile, O_RDWR | O_CREAT | O_BINARY);
612 gfd = open (binbookfile, O_RDWR | O_CREAT | O_BINARY, 0644);
614 ADMIN.bookcount = B.bookcount = 0;
615 ADMIN.booksize = B.booksize = booksize;
616 B.maxoffset = ADMIN.maxoffset = MAXOFFSET(B);
623 write (gfd, (char *)&ADMIN, sizeof_gdxadmin);
624 printf ("creating bookfile %s %ld %d\n", binbookfile, B.maxoffset, B.booksize);
625 for (x = 0; x < B.booksize; x++)
627 write (gfd, (char *)&DATA, sizeof_gdxdata);
636 /* setvbuf(fd,buffr,_IOFBF,2048); */
639 hashbd = hashkey = 0;
642 while ((c = Vparse (fd, &mv, &flags, side, i)) >= 0)
648 * if not first move of an opening and first
649 * time we have seen it save next move as
653 if (i < bookmaxply + 2)
655 if (i > 1 && !(flags & BADMOVE)) {
658 if (i < bookmaxply + 1)
661 * see if this position and
662 * move already exist from
667 HashOffset(bhashkey,B);
670 if (!ReadData(&DATA)) break; /* corrupted binbook file */
671 if (DATA.bmove == 0) break; /* free entry */
672 if (DATA.hashkey == HashValue(bhashkey) && DATA.hashbd == bhashbd) {
673 if (DATA.bmove == mv) {
675 * yes so just bump count - count is
676 * used to choose opening move in
677 * proportion to its presence in the book
684 if ( first ) collisions++;
685 if (DATA.flags & LASTMOVE) {
686 DATA.flags &= (~LASTMOVE);
697 * doesn`t exist so add it to
704 #if defined THINK_C || defined MSDOS
705 if (B.bookcount % 100 == 0)
707 if (B.bookcount % 1000 == 0)
709 printf ("%d rec %d openings processed\n", B.bookcount, games);
711 /* initialize a record */
712 DATA.hashbd = bhashbd;
713 DATA.hashkey = HashValue(bhashkey);
715 DATA.flags = flags | LASTMOVE;
723 opponent = computer ^ 1;
730 /* reset for next opening */
742 /* write admin rec with counts */
743 ADMIN.bookcount = B.bookcount;
749 if (binbookfile != NULL)
751 /* open book as reader */
752 gfd = open (binbookfile, O_RDONLY | O_BINARY);
755 if ( ReadAdmin() && (!ADMIN.booksize || ADMIN.maxoffset == MAXOFFSET(ADMIN)) )
757 B.bookcount = ADMIN.bookcount;
758 B.booksize = ADMIN.booksize;
759 B.maxoffset = ADMIN.maxoffset;
763 printf ("bad format %s\n", binbookfile);
771 B.booksize = booksize;
776 sprintf (msg, CP[213], B.bookcount, B.booksize);
778 /* printf("%ld collisions\n", collisions); */
781 /* set every thing back to start game */
784 /* now get ready to play */
788 ShowMessage (CP[212]);
796 OpeningBook (unsigned short *hint, short int side)
799 * Go thru each of the opening lines of play and check for a match with the
800 * current game listing. If a match occurs, generate a random number. If this
801 * number is the largest generated so far then the next move in this line
802 * becomes the current "candidate". After all lines are checked, the
803 * candidate move is put at the top of the Tree[] array and will be played by
804 * the program. Note that the program does not handle book transpositions.
809 int possibles = TrPnt[2] - TrPnt[1];
811 gsrand ((unsigned int) time ((long *) 0));
815 * find all the moves for this position - count them and get their
823 struct gdxdata OBB[128];
824 if (B.bookcount == 0)
830 HashOffset(hashkey,B);
832 printf("looking for book move, bhashbd = 0x%lx bhashkey = 0x%x\n", (ULONG)hashbd, HashValue(hashkey));
836 if (!ReadData(&OBB[x])) break;
837 if (OBB[x].bmove == 0) break;
839 printf("compare with bhashbd = 0x%lx bhashkey = 0x%x\n", OBB[x].hashbd, OBB[x].hashkey);
841 if (OBB[x].hashkey == HashValue(hashkey) && OBB[x].hashbd == (ULONG)hashbd)
844 if (OBB[x-1].flags & LASTMOVE) break;
849 printf("%d book move(s) found.\n",x);
856 for (i = 0; i < x; i++)
858 if (OBB[i].flags & BADMOVE)
861 /* is the move is in the MoveList */
862 for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
864 if (((Tree[b].f << 8) | Tree[b].t) == m)
868 Tree[b].score = DONTUSE;
877 movealgbr(m = OBB[i].bmove,s);
878 printf("finding book move: %s\n",s);
880 summ += OBB[i].count;
889 r = (urand () % summ);
890 for (i = 0; i < x; i++)
891 if (!(OBB[i].flags & BADMOVE) ){
892 if( r < OBB[i].count)
903 /* make sure the move is in the MoveList */
904 for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
906 if (((Tree[b].f << 8) | Tree[b].t) == m)
908 Tree[b].flags |= book;
913 /* Make sure its the best */
915 pick (TrPnt[1], TrPnt[2] - 1);
916 if (Tree[TrPnt[1]].score)
922 /* ok pick up the hint and go */