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