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
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 * ----------------------------------------------------------------------
38 /* Declarations of read(), write(), close(), and lseek(). */
48 unsigned booksize
= BOOKSIZE
;
49 unsigned short bookmaxply
= BOOKMAXPLY
;
50 unsigned bookcount
= 0;
53 char *bookfile
= BOOK
;
55 char *bookfile
= NULL
;
59 char *binbookfile
= BINBOOK
;
61 char *binbookfile
= NULL
;
64 static char bmvstr
[3][7];
67 static ULONG bhashkey
;
73 * Generate move strings in different formats.
77 Balgbr(short f
, short t
, short flags
)
79 short promoted
= false;
90 piece
= f
- NO_SQUARES
;
92 if (f
> (NO_SQUARES
+ NO_PIECES
))
95 flags
= (dropmask
| piece
);
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';
114 if ((flags
& dropmask
) != 0)
116 /* bmvstr[0]: P*3c bmvstr[1]: P'3c */
117 short piece
= flags
& pmask
;
118 bmvstr
[0][0] = pxx
[piece
];
120 bmvstr
[0][2] = COL_NAME(column(t
));
121 bmvstr
[0][3] = ROW_NAME(row(t
));
122 bmvstr
[0][4] = bmvstr
[2][0] = '\0';
123 strcpy(bmvstr
[1], bmvstr
[0]);
128 if ((f
!= 0) || (t
!= 0))
130 /* algebraic notation */
131 /* bmvstr[0]: 7g7f bmvstr[1]:
132 * (+)P7g7f(+) bmvstr[2]: (+)P7f(+) */
133 bmvstr
[0][0] = COL_NAME(column(f
));
134 bmvstr
[0][1] = ROW_NAME(row(f
));
135 bmvstr
[0][2] = COL_NAME(column(t
));
136 bmvstr
[0][3] = ROW_NAME(row(t
));
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]);
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]);
155 strcat(bmvstr
[0], "+");
156 strcat(bmvstr
[1], "+");
157 strcat(bmvstr
[2], "+");
162 bmvstr
[0][0] = bmvstr
[1][0] = bmvstr
[2][0] = '\0';
171 bkdisplay(char *s
, int cnt
, int moveno
)
178 printf("matches = %d\n", cnt
);
179 printf("inout move is :%s: move number %d side %s\n",
180 s
, moveno
/ 2 + 1, (moveno
& 1) ? "white" : "black");
182 #ifndef SEMIQUIETBOOKGEN
183 printf("legal moves are \n");
185 while (pnt
< TrPnt
[3])
189 if (is_promoted
[board
[node
->f
]] )
190 Balgbr(node
->f
| 0x80, node
->t
, (short) node
->flags
);
192 Balgbr(node
->f
, node
->t
, (short) node
->flags
);
195 bmvstr
[0], bmvstr
[1], bmvstr
[2]);
198 printf("\n current board is\n");
200 for (r
= (NO_ROWS
- 1); r
>= 0; r
--)
202 for (c
= 0; c
<= (NO_COLS
- 1); c
++)
207 pc
= (is_promoted
[board
[l
]] ? '+' : ' ');
209 if (color
[l
] == neutral
)
211 else if (color
[l
] == black
)
212 printf("%c%c", pc
, qxx
[board
[l
]]);
214 printf("%c%c", pc
, pxx
[board
[l
]]);
224 for (color
= black
; color
<= white
; color
++)
228 printf((color
== black
) ? "black " : "white ");
230 for (piece
= pawn
; piece
<= king
; piece
++)
232 if ((c
= Captured
[color
][piece
]))
233 printf("%i%c ", c
, pxx
[piece
]);
239 #endif /* SEMIQUIETBOOKGEN */
241 #endif /* QUIETBOOKGEN */
245 * BVerifyMove(s, mv, moveno)
247 * Compare the string 's' to the list of legal moves available for the
248 * opponent. If a match is found, make the move on the board.
252 BVerifyMove(char *s
, unsigned short *mv
, int moveno
)
254 static short pnt
, tempb
, tempc
, tempsf
, tempst
, cnt
;
255 static struct leaf xnode
;
260 MoveList(opponent
, 2, -2, true);
263 while (pnt
< TrPnt
[3])
267 if (is_promoted
[board
[node
->f
]] )
268 Balgbr(node
->f
| 0x80, node
->t
, (short) node
->flags
);
270 Balgbr(node
->f
, node
->t
, (short) node
->flags
);
272 if (strcmp(s
, bmvstr
[0]) == 0 || strcmp(s
, bmvstr
[1]) == 0 ||
273 strcmp(s
, bmvstr
[2]) == 0)
284 MakeMove(opponent
, &xnode
, &tempb
,
285 &tempc
, &tempsf
, &tempst
, &INCscore
);
287 if (SqAttacked(PieceList
[opponent
][0], computer
, &blockable
))
289 UnmakeMove(opponent
, &xnode
, &tempb
, &tempc
, &tempsf
, &tempst
);
290 /* Illegal move in check */
291 #if !defined QUIETBOOKGEN
292 puts("Illegal move (in check): %s");
293 bkdisplay(s
, cnt
, moveno
);
299 *mv
= (xnode
.f
<< 8) | xnode
.t
;
301 if (is_promoted
[board
[xnode
.t
]] )
302 Balgbr(xnode
.f
| 0x80, xnode
.t
, 0);
304 Balgbr(xnode
.f
, xnode
.t
, 0);
311 #if !defined QUIETBOOKGEN
312 printf("Illegal move (no match): %s\n", s
);
313 bkdisplay(s
, cnt
, moveno
);
322 * Reset the board and other variables to start a new game.
331 flag
.illegal
= flag
.mate
= flag
.quit
332 = flag
.reverse
= flag
.bothsides
= flag
.onemove
= flag
.force
335 flag
.post
&= xboard
; /* [HGM] xboard: do not clear in XBoard mode */
337 flag
.material
= flag
.coords
= flag
.hash
= flag
.easy
338 = flag
.beep
= flag
.rcptr
341 flag
.stars
= flag
.shade
= flag
.back
= flag
.musttimeout
= false;
345 CptrFlag
[0] = TesujiFlag
[0] = false;
349 for (l
= 0; l
< NO_SQUARES
; l
++)
351 board
[l
] = Stboard
[l
];
352 color
[l
] = Stcolor
[l
];
358 hashbd
= hashkey
= 0;
363 Vparse (FILE * fd
, USHORT
*mv
, USHORT
*flags
, int moveno
)
372 while (((c
= getc(fd
)) == ' ')
373 || (c
== '!') || (c
== '/') || (c
== '\n'));
377 /* amount of time spent for the last move */
378 while (((c
= getc(fd
)) != ')') && (c
!= EOF
));
382 while (((c
= getc(fd
)) == ' ') || (c
== '\n'));
388 /* comment for the actual game */
389 while (((c
= getc(fd
))) != ']' && (c
!= EOF
));
393 while (((c
= getc(fd
))) == ' ' || (c
== '\n'));
409 /* goes to end of line */
422 while ((c
>= '0') && (c
<= '9'))
430 while (((c
= getc(fd
)) == ' ') || (c
== '.') || (c
== '\n'));
434 while (((c
= getc(fd
)) != '?') && (c
!= '!') && (c
!= ' ')
435 && (c
!= '(') && (c
!= '\n') && (c
!= '\t') && (c
!= EOF
))
440 if ((c
!= 'x') && (c
!= '-') && (c
!= ',')
441 && (c
!= ';') && (c
!= '='))
451 while (((c
= getc(fd
)) != ')') && (c
!= EOF
));
462 while ((c
!= '\n') && (c
!= EOF
))
471 if (strcmp(s
, "draw") == 0)
473 else if (strcmp(s
, "1-0") == 0)
475 else if (strcmp(s
, "0-1") == 0)
477 else if (strcmp(s
, "Resigns") == 0)
479 else if (strcmp(s
, "Resigns.") == 0)
481 else if (strcmp(s
, "Sennichite") == 0)
483 else if (strcmp(s
, "Sennichite.") == 0)
485 else if (strcmp(s
, "Jishogi") == 0)
487 else if (strcmp(s
, "Jishogi.") == 0)
493 i
= BVerifyMove(s
, mv
, moveno
);
497 /* Bad move, not for the program to play */
498 *flags
|= BADMOVE
; /* Flag it ! */
499 while (((c
= getc(fd
)) == '?') || (c
== '!') || (c
== '/'));
504 /* Do not use by computer */
505 *flags
|= BADMOVE
; /* Flag it ! */
507 while (((c
= getc(fd
)) == '?') || (c
== '!') || (c
== '/'));
513 *flags
|= GOODMOVE
; /* Flag it ! */
515 while (((c
= getc(fd
)) == '?') || (c
== '!') || (c
== '/'));
523 while (((c
= getc(fd
)) != ')') && (c
!= EOF
));
527 /* flush to start of next */
528 while (((c
= getc(fd
)) != '#') && (c
!= EOF
));
546 static struct gdxadmin ADMIN
;
548 static struct gdxdata DATA
;
550 /* lts(l) returns most significant 16 bits of l */
552 #if SIZEOF_LONG == 8 /* 64-bit long i.e. 8 bytes */
553 # define lts(x) (USHORT)(((x >> 48) & 0xfffe) | side)
555 # if defined USE_LTSIMP
556 static USHORT
ltsimp(long x
)
559 n
= (((x
>> 16) & 0xfffe));
562 # define lts(x) (USHORT)(ltsimp(x) | side)
564 # define lts(x) (USHORT)(((x >> 16)&0xfffe) | side)
569 /* #define HashValue(l) lts(l) */
570 #define HashValue(l) (USHORT)(l & 0xffff)
574 #define MAXOFFSET(B) ((B.booksize - 1) * sizeof_gdxdata + sizeof_gdxadmin)
576 static ULONG
HashOffset(ULONG hashkey
, struct gdxadmin
*B
)
578 return (hashkey
% B
->booksize
) * sizeof_gdxdata
+ sizeof_gdxadmin
;
582 static ULONG
NextOffset(struct gdxadmin
*B
, ULONG offset
)
584 offset
+= sizeof_gdxdata
;
585 if (offset
> B
->maxoffset
)
586 offset
= sizeof_gdxadmin
;
591 static void WriteAdmin(void)
593 lseek(gfd
, 0, SEEK_SET
);
594 write(gfd
, (char *)&ADMIN
, sizeof_gdxadmin
);
597 static void WriteData(ULONG offset
, int *mustwrite
)
602 lseek(gfd
, offset
, SEEK_SET
);
603 write(gfd
, (char *)&DATA
, sizeof_gdxdata
);
607 static int ReadAdmin(void)
609 lseek(gfd
, 0, SEEK_SET
);
610 return (sizeof_gdxadmin
== read(gfd
, (char *)&ADMIN
, sizeof_gdxadmin
));
613 static int ReadData(ULONG offset
, struct gdxdata
*DATA
)
615 lseek(gfd
, offset
, SEEK_SET
);
616 return (sizeof_gdxdata
== read(gfd
, (char *)DATA
, sizeof_gdxdata
));
623 * CHECKME: is this still valid esp. wrt gnushogi.book?
625 * Read in the Opening Book file and parse the algebraic notation for a move
626 * into an unsigned integer format indicating the from and to square. Create
627 * a linked list of opening lines of play, with entry->next pointing to the
628 * next line and entry->move pointing to a chunk of memory containing the
629 * moves. More Opening lines of up to 100 half moves may be added to
630 * gnushogi.book. But now it's a hashed table by position which yields a move
631 * or moves for each position. It no longer knows about openings per se only
632 * positions and recommended moves in those positions.
639 ULONG currentoffset
= 0;
646 unsigned int games
= 0;
652 if ((fd
= fopen(bookfile
, "r")) == NULL
)
653 fd
= fopen("gnushogi.tbk", "r");
657 /* yes add to book */
658 /* open book as writer */
659 gfd
= open(binbookfile
, O_RDONLY
| O_BINARY
);
665 B
.bookcount
= ADMIN
.bookcount
;
666 B
.booksize
= ADMIN
.booksize
;
667 B
.maxoffset
= ADMIN
.maxoffset
;
669 if (B
.booksize
&& !(B
.maxoffset
== MAXOFFSET(B
)))
671 printf("bad format %s\n", binbookfile
);
677 printf("bad format %s\n", binbookfile
);
681 gfd
= open(binbookfile
, O_RDWR
| O_BINARY
);
686 gfd
= open(binbookfile
, O_RDWR
| O_CREAT
| O_BINARY
, 0644);
688 ADMIN
.bookcount
= B
.bookcount
= 0;
689 ADMIN
.booksize
= B
.booksize
= booksize
;
690 B
.maxoffset
= ADMIN
.maxoffset
= MAXOFFSET(B
);
698 printf("creating bookfile %s %ld %ld\n",
699 binbookfile
, B
.maxoffset
, B
.booksize
);
701 for (x
= 0; x
< B
.booksize
; x
++)
703 int mustwrite
= true;
704 WriteData(sizeof_gdxadmin
+ x
* sizeof_gdxdata
, &mustwrite
);
710 int mustwrite
= false;
711 /* setvbuf(fd, buffr, _IOFBF, 2048); */
713 hashbd
= hashkey
= 0;
716 while ((c
= Vparse(fd
, &mv
, &flags
, i
)) >= 0)
721 * If this is not the first move of an opening and
722 * if it's the first time we have seen it then
723 * save the next move as a hint.
727 if (i
< bookmaxply
+ 2)
729 if (i
> 1 && !(flags
& BADMOVE
))
732 if (i
< bookmaxply
+ 1)
735 * See if this position and move already
736 * exist from some other opening.
739 WriteData(currentoffset
, &mustwrite
);
740 currentoffset
= HashOffset(bhashkey
, &B
);
745 if (!ReadData(currentoffset
, &DATA
))
746 break; /* corrupted binbook file */
749 break; /* free entry */
751 if (DATA
.hashkey
== HashValue(bhashkey
)
752 && DATA
.hashbd
== bhashbd
)
754 if (DATA
.bmove
== mv
)
757 * Yes, so just bump count - count
758 * is used to choose the opening
759 * move in proportion to its
760 * presence in the book.
773 if (DATA
.flags
& LASTMOVE
)
775 DATA
.flags
&= (~LASTMOVE
);
777 WriteData(currentoffset
, &mustwrite
);
782 currentoffset
= NextOffset(&B
, currentoffset
);
787 * Doesn't exist so add it to the book.
794 if ((B
.bookcount
% 1000) == 0)
796 /* CHECKME: may want to get rid of this,
797 * especially for xshogi. */
798 printf("%ld rec %d openings "
803 /* initialize a record */
804 DATA
.hashbd
= bhashbd
;
805 DATA
.hashkey
= HashValue(bhashkey
);
807 DATA
.flags
= flags
| LASTMOVE
;
816 opponent
= computer
^ 1;
822 /* reset for next opening */
824 WriteData(currentoffset
, &mustwrite
);
831 WriteData(currentoffset
, &mustwrite
);
833 /* write admin rec with counts */
834 ADMIN
.bookcount
= B
.bookcount
;
841 if (binbookfile
!= NULL
)
843 /* open book as reader */
844 gfd
= open(binbookfile
, O_RDONLY
| O_BINARY
);
848 if (ReadAdmin() && (!ADMIN
.booksize
849 || (ADMIN
.maxoffset
== MAXOFFSET(ADMIN
))))
851 B
.bookcount
= ADMIN
.bookcount
;
852 B
.booksize
= ADMIN
.booksize
;
853 B
.maxoffset
= ADMIN
.maxoffset
;
857 printf("bad format %s\n", binbookfile
);
865 B
.booksize
= booksize
;
869 sprintf(msg
, "Book used %lu(%lu).", B
.bookcount
, B
.booksize
);
870 dsp
->ShowMessage(msg
);
873 /* Set everything back to start the game. */
877 /* Now get ready to play .*/
880 dsp
->ShowMessage("Can't find book.");
889 * Go through each of the opening lines of play and check for a match with
890 * the current game listing. If a match occurs, generate a random
891 * number. If this number is the largest generated so far then the next
892 * move in this line becomes the current "candidate". After all lines are
893 * checked, the candidate move is put at the top of the Tree[] array and
894 * will be played by the program. Note that the program does not handle
895 * book transpositions.
899 OpeningBook(unsigned short *hint
)
903 int possibles
= TrPnt
[2] - TrPnt
[1];
905 gsrand((unsigned int) time((long *) 0));
909 * Find all the moves for this position - count them and get their
918 struct gdxdata OBB
[128];
920 if (B
.bookcount
== 0)
927 currentoffset
= HashOffset(hashkey
, &B
);
929 printf("looking for book move, bhashbd = 0x%lx bhashkey = 0x%x\n",
930 (ULONG
)hashbd
, HashValue(hashkey
));
934 if (!ReadData(currentoffset
, &OBB
[x
]))
937 if (OBB
[x
].bmove
== 0)
941 printf("compare with bhashbd = 0x%lx bhashkey = 0x%x\n",
942 OBB
[x
].hashbd
, OBB
[x
].hashkey
);
944 if ((OBB
[x
].hashkey
== HashValue(hashkey
))
945 && (OBB
[x
].hashbd
== (ULONG
)hashbd
))
949 if (OBB
[x
-1].flags
& LASTMOVE
)
953 currentoffset
= NextOffset(&B
, currentoffset
);
957 printf("%d book move(s) found.\n", x
);
966 for (i
= 0; i
< x
; i
++)
968 if (OBB
[i
].flags
& BADMOVE
)
972 /* Is the move in the MoveList? */
973 for (b
= TrPnt
[1]; b
< (unsigned) TrPnt
[2]; b
++)
975 if (((Tree
[b
].f
<< 8) | Tree
[b
].t
) == m
)
978 Tree
[b
].score
= DONTUSE
;
987 movealgbr(m
= OBB
[i
].bmove
, s
);
988 printf("finding book move: %s\n", s
);
990 summ
+= OBB
[i
].count
;
1000 r
= (urand() % summ
);
1002 for (i
= 0; i
< x
; i
++)
1004 if (!(OBB
[i
].flags
& BADMOVE
))
1006 if (r
< OBB
[i
].count
)
1021 /* Make sure the move is in the MoveList. */
1022 for (b
= TrPnt
[1]; b
< (unsigned) TrPnt
[2]; b
++)
1024 if (((Tree
[b
].f
<< 8) | Tree
[b
].t
) == m
)
1026 Tree
[b
].flags
|= book
;
1032 /* Make sure it's the best. */
1034 pick(TrPnt
[1], TrPnt
[2] - 1);
1036 if (Tree
[TrPnt
[1]].score
)
1043 /* Ok, pick up the hint and go. */