4
* ----------------------------------------------------------------------
5
* Copyright (c) 1993, 1994, 1995 Matthias Mutz
6
* Copyright (c) 1999 Michael Vanier and the Free Software Foundation
8
* GNU SHOGI is based on GNU CHESS
10
* Copyright (c) 1988, 1989, 1990 John Stanback
11
* Copyright (c) 1992 Free Software Foundation
13
* This file is part of GNU SHOGI.
15
* GNU Shogi is free software; you can redistribute it and/or modify it
16
* under the terms of the GNU General Public License as published by the
17
* Free Software Foundation; either version 1, or (at your option) any
20
* GNU Shogi is distributed in the hope that it will be useful, but WITHOUT
21
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
22
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25
* You should have received a copy of the GNU General Public License along
26
* with GNU Shogi; see the file COPYING. If not, write to the Free
27
* Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
28
* ----------------------------------------------------------------------
37
/* Declarations of read(), write(), close(), and lseek(). */
47
unsigned booksize = BOOKSIZE;
48
unsigned short bookmaxply = BOOKMAXPLY;
49
unsigned bookcount = 0;
52
char *bookfile = BOOK;
54
char *bookfile = NULL;
58
char *binbookfile = BINBOOK;
60
char *binbookfile = NULL;
63
static char bmvstr[3][7];
66
static ULONG bhashkey;
72
* Generate move strings in different formats.
76
Balgbr(short f, short t, short flag)
78
short promoted = false;
89
piece = f - NO_SQUARES;
91
if (f > (NO_SQUARES + NO_PIECES))
94
flag = (dropmask | piece);
103
if ((f == t) && ((f != 0) || (t != 0)))
106
* error in algbr: FROM=TO=t
109
bmvstr[0][0] = bmvstr[1][0] = bmvstr[2][0] = '\0';
113
if ((flag & dropmask) != 0)
115
/* bmvstr[0]: P*3c bmvstr[1]: P'3c */
116
short piece = flag & pmask;
117
bmvstr[0][0] = pxx[piece];
119
bmvstr[0][2] = cxx[column(t)];
120
bmvstr[0][3] = rxx[row(t)];
121
bmvstr[0][4] = bmvstr[2][0] = '\0';
122
strcpy(bmvstr[1], bmvstr[0]);
127
if ((f != 0) || (t != 0))
129
/* algebraic notation */
130
/* bmvstr[0]: 7g7f bmvstr[1]:
131
* (+)P7g7f(+) bmvstr[2]: (+)P7f(+) */
132
bmvstr[0][0] = cxx[column(f)];
133
bmvstr[0][1] = rxx[row(f)];
134
bmvstr[0][2] = cxx[column(t)];
135
bmvstr[0][3] = rxx[row(t)];
140
bmvstr[1][0] = bmvstr[2][0] = '+';
141
bmvstr[1][1] = bmvstr[2][1] = pxx[board[f]];
142
strcpy(&bmvstr[1][2], &bmvstr[0][0]);
143
strcpy(&bmvstr[2][2], &bmvstr[0][2]);
147
bmvstr[1][0] = bmvstr[2][0] = pxx[board[f]];
148
strcpy(&bmvstr[1][1], &bmvstr[0][0]);
149
strcpy(&bmvstr[2][1], &bmvstr[0][2]);
154
strcat(bmvstr[0], "+");
155
strcat(bmvstr[1], "+");
156
strcat(bmvstr[2], "+");
161
bmvstr[0][0] = bmvstr[1][0] = bmvstr[2][0] = '\0';
172
bkdisplay(char *s, int cnt, int moveno)
179
printf("matches = %d\n", cnt);
180
printf("inout move is :%s: move number %d side %s\n",
181
s, moveno / 2 + 1, (moveno & 1) ? "white" : "black");
183
#ifndef SEMIQUIETBOOKGEN
184
printf("legal moves are \n");
186
while (pnt < TrPnt[3])
190
if (is_promoted[board[node->f]] )
191
Balgbr(node->f | 0x80, node->t, (short) node->flags);
193
Balgbr(node->f, node->t, (short) node->flags);
196
bmvstr[0], bmvstr[1], bmvstr[2]);
199
printf("\n current board is\n");
201
for (r = (NO_ROWS - 1); r >= 0; r--)
203
for (c = 0; c <= (NO_COLS - 1); c++)
208
pc = (is_promoted[board[l]] ? '+' : ' ');
210
if (color[l] == neutral)
212
else if (color[l] == black)
213
printf("%c%c", pc, qxx[board[l]]);
215
printf("%c%c", pc, pxx[board[l]]);
225
for (color = black; color <= white; color++)
229
printf((color == black) ? "black " : "white ");
231
for (piece = pawn; piece <= king; piece++)
233
if ((c = Captured[color][piece]))
234
printf("%i%c ", c, pxx[piece]);
240
#endif /* SEMIQUIETBOOKGEN */
243
#endif /* QUIETBOOKGEN */
248
* BVerifyMove(s, mv, moveno)
250
* Compare the string 's' to the list of legal moves available for the
251
* opponent. If a match is found, make the move on the board.
255
BVerifyMove(char *s, unsigned short *mv, int moveno)
257
static short pnt, tempb, tempc, tempsf, tempst, cnt;
258
static struct leaf xnode;
263
MoveList(opponent, 2, -2, true);
266
while (pnt < TrPnt[3])
270
if (is_promoted[board[node->f]] )
271
Balgbr(node->f | 0x80, node->t, (short) node->flags);
273
Balgbr(node->f, node->t, (short) node->flags);
275
if (strcmp(s, bmvstr[0]) == 0 || strcmp(s, bmvstr[1]) == 0 ||
276
strcmp(s, bmvstr[2]) == 0)
287
MakeMove(opponent, &xnode, &tempb,
288
&tempc, &tempsf, &tempst, &INCscore);
290
if (SqAttacked(PieceList[opponent][0], computer, &blockable))
292
UnmakeMove(opponent, &xnode, &tempb, &tempc, &tempsf, &tempst);
293
/* Illegal move in check */
294
#if !defined QUIETBOOKGEN
295
/* 077: "Illegal move (in check) %s" */
298
bkdisplay(s, cnt, moveno);
304
*mv = (xnode.f << 8) | xnode.t;
306
if (is_promoted[board[xnode.t]] )
307
Balgbr(xnode.f | 0x80, xnode.t, false);
309
Balgbr(xnode.f, xnode.t, false);
316
#if !defined QUIETBOOKGEN
317
/* 075: "Illegal move (no match)%s\n" */
319
bkdisplay(s, cnt, moveno);
330
* Reset the board and other variables to start a new game.
339
flag.illegal = flag.mate = flag.post = flag.quit
340
= flag.reverse = flag.bothsides = flag.onemove = flag.force
343
flag.material = flag.coords = flag.hash = flag.easy
344
= flag.beep = flag.rcptr
347
flag.stars = flag.shade = flag.back = flag.musttimeout = false;
351
CptrFlag[0] = TesujiFlag[0] = false;
355
for (l = 0; l < NO_SQUARES; l++)
357
board[l] = Stboard[l];
358
color[l] = Stcolor[l];
364
hashbd = hashkey = 0;
371
Vparse (FILE * fd, USHORT *mv, USHORT *flags, USHORT side, int moveno)
380
while (((c = getc(fd)) == ' ')
381
|| (c == '!') || (c == '/') || (c == '\n'));
385
/* amount of time spent for the last move */
386
while (((c = getc(fd)) != ')') && (c != EOF));
390
while (((c = getc(fd)) == ' ') || (c == '\n'));
396
/* comment for the actual game */
397
while (((c = getc(fd))) != ']' && (c != EOF));
401
while (((c = getc(fd))) == ' ' || (c == '\n'));
417
/* goes to end of line */
430
while ((c >= '0') && (c <= '9'))
438
while (((c = getc(fd)) == ' ') || (c == '.') || (c == '\n'));
442
while (((c = getc(fd)) != '?') && (c != '!') && (c != ' ')
443
&& (c != '(') && (c != '\n') && (c != '\t') && (c != EOF))
448
if ((c != 'x') && (c != '-') && (c != ',')
449
&& (c != ';') && (c != '='))
459
while (((c = getc(fd)) != ')') && (c != EOF));
470
while ((c != '\n') && (c != EOF))
479
if (strcmp(s, "draw") == 0)
481
else if (strcmp(s, "1-0") == 0)
483
else if (strcmp(s, "0-1") == 0)
485
else if (strcmp(s, "Resigns") == 0)
487
else if (strcmp(s, "Resigns.") == 0)
489
else if (strcmp(s, "Sennichite") == 0)
491
else if (strcmp(s, "Sennichite.") == 0)
493
else if (strcmp(s, "Jishogi") == 0)
495
else if (strcmp(s, "Jishogi.") == 0)
501
i = BVerifyMove(s, mv, moveno);
505
/* Bad move, not for the program to play */
506
*flags |= BADMOVE; /* Flag it ! */
507
while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
512
/* Do not use by computer */
513
*flags |= BADMOVE; /* Flag it ! */
515
while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
521
*flags |= GOODMOVE; /* Flag it ! */
523
while (((c = getc(fd)) == '?') || (c == '!') || (c == '/'));
531
while (((c = getc(fd)) != ')') && (c != EOF));
535
/* flush to start of next */
536
while (((c = getc(fd)) != '#') && (c != EOF));
554
static struct gdxadmin ADMIN;
556
static struct gdxdata DATA;
558
/* lts(l) returns most significant 16 bits of l */
560
#if SIZEOF_LONG == 8 /* 64-bit long i.e. 8 bytes */
561
# define lts(x) (USHORT)(((x >> 48) & 0xfffe) | side)
563
# if defined USE_LTSIMP
564
static USHORT ltsimp(long x)
567
n = (((x >> 16) & 0xfffe));
570
# define lts(x) (USHORT)(ltsimp(x) | side)
572
# define lts(x) (USHORT)(((x >> 16)&0xfffe) | side)
577
/* #define HashValue(l) lts(l) */
578
#define HashValue(l) (USHORT)(l & 0xffff)
582
static ULONG currentoffset;
585
#define MAXOFFSET(B) ((B.booksize - 1) * sizeof_gdxdata + sizeof_gdxadmin)
587
#define HashOffset(hashkey, B) \
589
currentoffset = ((ULONG)hashkey % B.booksize) \
590
* sizeof_gdxdata + sizeof_gdxadmin; \
594
#define NextOffset(B) \
596
currentoffset += sizeof_gdxdata; \
597
if (currentoffset > B.maxoffset) \
598
currentoffset = sizeof_gdxadmin; \
602
#define WriteAdmin() \
605
write(gfd, (char *)&ADMIN, sizeof_gdxadmin); \
608
#define WriteData() \
612
lseek(gfd, currentoffset, 0); \
613
write(gfd, (char *)&DATA, sizeof_gdxdata); \
618
static int ReadAdmin(void)
621
return (sizeof_gdxadmin == read(gfd, (char *)&ADMIN, sizeof_gdxadmin));
624
static int ReadData(struct gdxdata *DATA)
626
lseek(gfd, currentoffset, 0);
627
return (sizeof_gdxdata == read(gfd, (char *)DATA, sizeof_gdxdata));
634
* CHECKME: is this still valid esp. wrt gnushogi.book?
636
* Read in the Opening Book file and parse the algebraic notation for a move
637
* into an unsigned integer format indicating the from and to square. Create
638
* a linked list of opening lines of play, with entry->next pointing to the
639
* next line and entry->move pointing to a chunk of memory containing the
640
* moves. More Opening lines of up to 100 half moves may be added to
641
* gnushogi.book. But now it's a hashed table by position which yields a move
642
* or moves for each position. It no longer knows about openings per se only
643
* positions and recommended moves in those positions.
651
int mustwrite = false, first;
652
unsigned short xside, side;
656
unsigned int games = 0;
662
if ((fd = fopen(bookfile, "r")) == NULL)
663
fd = fopen("gnushogi.tbk", "r");
667
/* yes add to book */
668
/* open book as writer */
669
gfd = open(binbookfile, O_RDONLY | O_BINARY);
675
B.bookcount = ADMIN.bookcount;
676
B.booksize = ADMIN.booksize;
677
B.maxoffset = ADMIN.maxoffset;
679
if (B.booksize && !(B.maxoffset == MAXOFFSET(B)))
681
printf("bad format %s\n", binbookfile);
687
printf("bad format %s\n", binbookfile);
691
gfd = open(binbookfile, O_RDWR | O_BINARY);
696
gfd = open(binbookfile, O_RDWR | O_CREAT | O_BINARY, 0644);
698
ADMIN.bookcount = B.bookcount = 0;
699
ADMIN.booksize = B.booksize = booksize;
700
B.maxoffset = ADMIN.maxoffset = MAXOFFSET(B);
707
write(gfd, (char *)&ADMIN, sizeof_gdxadmin);
708
printf("creating bookfile %s %ld %ld\n",
709
binbookfile, B.maxoffset, B.booksize);
711
for (x = 0; x < B.booksize; x++)
713
write(gfd, (char *)&DATA, sizeof_gdxdata);
719
/* setvbuf(fd, buffr, _IOFBF, 2048); */
722
hashbd = hashkey = 0;
725
while ((c = Vparse(fd, &mv, &flags, side, i)) >= 0)
730
* If this is not the first move of an opening and
731
* if it's the first time we have seen it then
732
* save the next move as a hint.
736
if (i < bookmaxply + 2)
738
if (i > 1 && !(flags & BADMOVE))
741
if (i < bookmaxply + 1)
744
* See if this position and move already
745
* exist from some other opening.
749
HashOffset(bhashkey, B);
754
if (!ReadData(&DATA))
755
break; /* corrupted binbook file */
758
break; /* free entry */
760
if (DATA.hashkey == HashValue(bhashkey)
761
&& DATA.hashbd == bhashbd)
763
if (DATA.bmove == mv)
766
* Yes, so just bump count - count
767
* is used to choose the opening
768
* move in proportion to its
769
* presence in the book.
782
if (DATA.flags & LASTMOVE)
784
DATA.flags &= (~LASTMOVE);
796
* Doesn't exist so add it to the book.
803
if ((B.bookcount % 1000) == 0)
805
/* CHECKME: may want to get rid of this,
806
* especially for xshogi. */
807
printf("%ld rec %d openings "
812
/* initialize a record */
813
DATA.hashbd = bhashbd;
814
DATA.hashkey = HashValue(bhashkey);
816
DATA.flags = flags | LASTMOVE;
825
opponent = computer ^ 1;
832
/* reset for next opening */
845
/* write admin rec with counts */
846
ADMIN.bookcount = B.bookcount;
853
if (binbookfile != NULL)
855
/* open book as reader */
856
gfd = open(binbookfile, O_RDONLY | O_BINARY);
860
if (ReadAdmin() && (!ADMIN.booksize
861
|| (ADMIN.maxoffset == MAXOFFSET(ADMIN))))
863
B.bookcount = ADMIN.bookcount;
864
B.booksize = ADMIN.booksize;
865
B.maxoffset = ADMIN.maxoffset;
869
printf("bad format %s\n", binbookfile);
877
B.booksize = booksize;
881
/* 213: "Book used %d(%d)." */
882
sprintf(msg, CP[213], B.bookcount, B.booksize);
886
/* Set everything back to start the game. */
890
/* Now get ready to play .*/
893
/* 212: "Can't find book." */
894
ShowMessage(CP[212]);
902
* OpeningBook(hint, side)
904
* Go through each of the opening lines of play and check for a match with
905
* the current game listing. If a match occurs, generate a random
906
* number. If this number is the largest generated so far then the next
907
* move in this line becomes the current "candidate". After all lines are
908
* checked, the candidate move is put at the top of the Tree[] array and
909
* will be played by the program. Note that the program does not handle
910
* book transpositions.
914
OpeningBook(unsigned short *hint, short side)
917
int possibles = TrPnt[2] - TrPnt[1];
919
gsrand((unsigned int) time((long *) 0));
923
* Find all the moves for this position - count them and get their
932
struct gdxdata OBB[128];
934
if (B.bookcount == 0)
941
HashOffset(hashkey, B);
943
printf("looking for book move, bhashbd = 0x%lx bhashkey = 0x%x\n",
944
(ULONG)hashbd, HashValue(hashkey));
948
if (!ReadData(&OBB[x]))
951
if (OBB[x].bmove == 0)
955
printf("compare with bhashbd = 0x%lx bhashkey = 0x%x\n",
956
OBB[x].hashbd, OBB[x].hashkey);
958
if ((OBB[x].hashkey == HashValue(hashkey))
959
&& (OBB[x].hashbd == (ULONG)hashbd))
963
if (OBB[x-1].flags & LASTMOVE)
971
printf("%d book move(s) found.\n", x);
980
for (i = 0; i < x; i++)
982
if (OBB[i].flags & BADMOVE)
986
/* Is the move in the MoveList? */
987
for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
989
if (((Tree[b].f << 8) | Tree[b].t) == m)
992
Tree[b].score = DONTUSE;
1001
movealgbr(m = OBB[i].bmove, s);
1002
printf("finding book move: %s\n", s);
1004
summ += OBB[i].count;
1014
r = (urand() % summ);
1016
for (i = 0; i < x; i++)
1018
if (!(OBB[i].flags & BADMOVE))
1020
if (r < OBB[i].count)
1035
/* Make sure the move is in the MoveList. */
1036
for (b = TrPnt[1]; b < (unsigned) TrPnt[2]; b++)
1038
if (((Tree[b].f << 8) | Tree[b].t) == m)
1040
Tree[b].flags |= book;
1046
/* Make sure it's the best. */
1048
pick(TrPnt[1], TrPnt[2] - 1);
1050
if (Tree[TrPnt[1]].score)
1057
/* Ok, pick up the hint and go. */