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 */