1 /* epd.c
2
3 GNU Chess frontend
4
5 Copyright (C) 2001-2021 Free Software Foundation, Inc.
6
7 GNU Chess is based on the two research programs
8 Cobalt by Chua Kong-Sian and Gazebo by Stuart Cracraft.
9
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23 Contact Info:
24 bug-gnu-chess@gnu.org
25 cracraft@ai.mit.edu, cracraft@stanfordalumni.org, cracraft@earthlink.net
26 */
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include "common.h"
32
33 #define EPDCLOSE 1
34
35 /* A line read from an EPD file, used in solve.c */
36 char epd_line[MAXSTR]="";
37
ReadEPDFile(const char * file,short op)38 short ReadEPDFile (const char *file, short op)
39 /****************************************************************************
40 *
41 * Reads in an EPD file. The first call will read the first EPD line,
42 * the second call will read the 2nd line and so on. To improve
43 * performance, the file is never closed. Closing of the file happens
44 * only on 2 conditions: (i) A ReadEPDFile failed because there is no
45 * more lines to read. (ii) A called to ReadEPDFile to explicitly
46 * request that the file is closed (op = EPDCLOSE);
47 * If op == 2, then we work silently.
48 * Comment lines are stripped off. Comment mark is '#'.
49 *
50 ****************************************************************************/
51 {
52 static FILE *fp = NULL;
53 char line[MAXSTR];
54
55 /* If first time through, must open file */
56 if (fp == NULL)
57 {
58 fp = fopen (file, "r");
59 if (fp == NULL)
60 {
61 printf ("Error opening file %s\n", file);
62 return (false);
63 }
64 }
65
66 /* Is this a close request? */
67 if (op == EPDCLOSE)
68 {
69 fclose (fp);
70 fp = NULL;
71 return (false);
72 }
73
74 next_line:
75 /* Okay, we read in an EPD entry */
76 if ( fgets (line, MAXSTR-1, fp) == NULL ) {
77 /* Do nothing - just avoid compilation warning - further refactoring is tricky. */
78 }
79 strcpy( epd_line, line );
80 if (!feof(fp))
81 {
82 /* Skip comment lines */
83 unsigned int i=0;
84 for (i = 0; i < strlen( line ); ++i) {
85 if ( isblank( line[i] ) ) {
86 continue;
87 }
88 if ( line[i] == '#' ) {
89 goto next_line;
90 } else {
91 break;
92 }
93 }
94 int ret = ParseEPD (line);
95
96 /* For now just ignore malformed lines */
97 if (ret != EPD_SUCCESS) goto next_line;
98 if (op != 2)
99 printf ("\n%s : Best move = %s\n", id, solution);
100 return (true);
101 }
102 /* finished, must close file */
103 else
104 {
105 fclose (fp);
106 fp = NULL;
107 return (false);
108 }
109 }
110
111 /*
112 * Returns EPD_SUCCESS on success, EPD_ERROR on error. We try to be
113 * quite tough on the format. However, as of yet no legality checking
114 * is done and the board is not reset on error, this should be done by
115 * the caller.
116 */
117
ParseEPD(char * p)118 int ParseEPD (char *p)
119 /**************************************************************************
120 *
121 * Parses an EPD input line. A few global variables are updated e.g.
122 * current board, side to move, en passant, castling status, etc.
123 *
124 **************************************************************************/
125 {
126 int r, c, sq;
127 char *str_p;
128
129 r = 56;
130 c = 0;
131 memset (&board, 0, sizeof (board));
132
133 while (p && *p != ' ')
134 {
135 sq = r + c;
136 switch (*p)
137 {
138 case 'P' : SETBIT (board.b[white][pawn], sq);
139 SETBIT (board.blockerr90, r90[sq]);
140 SETBIT (board.blockerr45, r45[sq]);
141 SETBIT (board.blockerr315, r315[sq]);
142 board.material[white] += ValueP;
143 break;
144 case 'N' : SETBIT (board.b[white][knight], sq);
145 SETBIT (board.blockerr90, r90[sq]);
146 SETBIT (board.blockerr45, r45[sq]);
147 SETBIT (board.blockerr315, r315[sq]);
148 board.material[white] += ValueN;
149 break;
150 case 'B' : SETBIT (board.b[white][bishop], sq);
151 SETBIT (board.blockerr90, r90[sq]);
152 SETBIT (board.blockerr45, r45[sq]);
153 SETBIT (board.blockerr315, r315[sq]);
154 board.material[white] += ValueB;
155 break;
156 case 'R' : SETBIT (board.b[white][rook], sq);
157 SETBIT (board.blockerr90, r90[sq]);
158 SETBIT (board.blockerr45, r45[sq]);
159 SETBIT (board.blockerr315, r315[sq]);
160 board.material[white] += ValueR;
161 break;
162 case 'Q' : SETBIT (board.b[white][queen], sq);
163 SETBIT (board.blockerr90, r90[sq]);
164 SETBIT (board.blockerr45, r45[sq]);
165 SETBIT (board.blockerr315, r315[sq]);
166 board.material[white] += ValueQ;
167 break;
168 case 'K' : SETBIT (board.b[white][king], sq);
169 SETBIT (board.blockerr90, r90[sq]);
170 SETBIT (board.blockerr45, r45[sq]);
171 SETBIT (board.blockerr315, r315[sq]);
172 break;
173 case 'p' : SETBIT (board.b[black][pawn], sq);
174 SETBIT (board.blockerr90, r90[sq]);
175 SETBIT (board.blockerr45, r45[sq]);
176 SETBIT (board.blockerr315, r315[sq]);
177 board.material[black] += ValueP;
178 break;
179 case 'n' : SETBIT (board.b[black][knight], sq);
180 SETBIT (board.blockerr90, r90[sq]);
181 SETBIT (board.blockerr45, r45[sq]);
182 SETBIT (board.blockerr315, r315[sq]);
183 board.material[black] += ValueN;
184 break;
185 case 'b' : SETBIT (board.b[black][bishop], sq);
186 SETBIT (board.blockerr90, r90[sq]);
187 SETBIT (board.blockerr45, r45[sq]);
188 SETBIT (board.blockerr315, r315[sq]);
189 board.material[black] += ValueB;
190 break;
191 case 'r' : SETBIT (board.b[black][rook], sq);
192 SETBIT (board.blockerr90, r90[sq]);
193 SETBIT (board.blockerr45, r45[sq]);
194 SETBIT (board.blockerr315, r315[sq]);
195 board.material[black] += ValueR;
196 break;
197 case 'q' : SETBIT (board.b[black][queen], sq);
198 SETBIT (board.blockerr90, r90[sq]);
199 SETBIT (board.blockerr45, r45[sq]);
200 SETBIT (board.blockerr315, r315[sq]);
201 board.material[black] += ValueQ;
202 break;
203 case 'k' : SETBIT (board.b[black][king], sq);
204 SETBIT (board.blockerr90, r90[sq]);
205 SETBIT (board.blockerr45, r45[sq]);
206 SETBIT (board.blockerr315, r315[sq]);
207 break;
208 case '/' : r -= 8;
209 c = -1;
210 break;
211 default : break;
212 }
213 if (isdigit (*p))
214 c += (*p - '0');
215 else
216 c++;
217
218 /*
219 * Special case, a trailing "/" is accepted on the
220 * end of the board settings.
221 */
222
223 if (r == -8 && p[1] == ' ')
224 r = 0;
225
226 if (r < 0 || c > 8) return EPD_ERROR;
227 if (c == 8 && p[1] != '/' && p[1] != ' ') return EPD_ERROR;
228 p++;
229 }
230
231 board.pmaterial[white] = board.material[white] -
232 nbits(board.b[white][pawn]) * ValueP;
233 board.pmaterial[black] = board.material[black] -
234 nbits(board.b[black][pawn]) * ValueP;
235 board.king[white] = leadz (board.b[white][king]);
236 board.king[black] = leadz (board.b[black][king]);
237 UpdateFriends ();
238 UpdateCBoard ();
239 UpdateMvboard ();
240
241 /* Get side to move */
242 if (!++p) return EPD_ERROR;
243 if (*p == 'w') board.side = white;
244 else if (*p == 'b') board.side = black;
245 else return EPD_ERROR;
246
247 /* Isn't this one cute? */
248 if (!++p || *p != ' ' || !++p) return EPD_ERROR;
249
250 /* Castling status */
251 while (p && *p != ' ') {
252 if (*p == 'K') board.flag |= WKINGCASTLE;
253 else if (*p == 'Q') board.flag |= WQUEENCASTLE;
254 else if (*p == 'k') board.flag |= BKINGCASTLE;
255 else if (*p == 'q') board.flag |= BQUEENCASTLE;
256 else if (*p == '-') { p++; break; }
257 else return EPD_ERROR;
258 p++;
259 }
260 if (!p || *p != ' ' || !++p) return EPD_ERROR;
261
262 /*
263 * En passant square, can only be '-' or [a-h][36]
264 * In fact, one could add more sanity checks here.
265 */
266 if (*p != '-') {
267 if (!p[1] || *p < 'a' || *p > 'h' ||
268 !(p[1] == '3' || p[1] == '6')) return EPD_ERROR;
269 board.ep = (*p - 'a') + (p[1] - '1')*8;
270 p++;
271 } else {
272 board.ep = -1;
273 }
274
275 solution[0] = '\0';
276 id[0] = '\0';
277
278 if (!++p) return EPD_SUCCESS;
279
280 /* The opcodes are optional, so we should not generate errors here */
281
282 /* Read in best move; "bm" operator */
283 str_p = strstr(p, "bm");
284 if (str_p) sscanf (str_p, "bm %63[^;];", solution);
285
286 /* Read in the description; "id" operator */
287 str_p = strstr(p, "id");
288 if (str_p) sscanf (p, "id %31[^;];", id);
289
290 phase = PHASE;
291
292 return EPD_SUCCESS;
293 }
294
295
LoadEPD(char * p)296 void LoadEPD (char *p)
297 /**************************************************************************
298 *
299 * This routine reads in the next or the Nth position in the file.
300 *
301 **************************************************************************/
302 {
303 char file[MAXSTR];
304 int N = 1;
305
306 sscanf (p, "%127s %d ", file, &N);
307 if (strcmp (file, "next") == 0)
308 {
309 ReadEPDFile (file, 0);
310 }
311 else
312 {
313 ReadEPDFile (file, 1);
314 while (--N)
315 {
316 if (ReadEPDFile (file, 2) == false)
317 {
318 printf ("File position exceeded\n");
319 return;
320 }
321 }
322 ReadEPDFile (file, 0);
323 }
324 ShowBoard ();
325 NewPosition ();
326 }
327
328
EPD2str(char * pos)329 void EPD2str (char *pos)
330 /**************************************************************************
331 *
332 * This routine writes the current position in EPD format into a string.
333 *
334 **************************************************************************/
335 {
336 int r, c, sq, k;
337 char c1;
338
339 for (r = A8; r >= A1; r -= 8)
340 {
341 k = 0;
342 for (c = 0; c < 8; c++)
343 {
344 sq = r + c;
345 if (cboard[sq] == empty)
346 k++;
347 else
348 {
349 if (k)
350 sprintf(pos + strlen(pos), "%1d", k);
351 k = 0;
352 c1 = notation[cboard[sq]];
353 if (BitPosArray[sq] & board.friends[black])
354 c1 = tolower (c1);
355 sprintf(pos + strlen(pos), "%c", c1);
356 }
357 }
358 if (k)
359 sprintf(pos + strlen(pos), "%1d", k);
360 if (r > A1)
361 sprintf(pos + strlen(pos), "/");
362 }
363
364 /* Print other stuff */
365 sprintf(pos + strlen(pos), (board.side == white ? " w " : " b "));
366
367 if (board.flag & WKINGCASTLE)
368 sprintf(pos + strlen(pos), "K");
369 if (board.flag & WQUEENCASTLE)
370 sprintf(pos + strlen(pos), "Q");
371 if (board.flag & BKINGCASTLE)
372 sprintf(pos + strlen(pos), "k");
373 if (board.flag & BQUEENCASTLE)
374 sprintf(pos + strlen(pos), "q");
375 if (!(board.flag & (WCASTLE | BCASTLE)))
376 sprintf(pos + strlen(pos), "-");
377
378 sprintf(pos + strlen(pos), " %s", (board.ep > -1 ? algbr[board.ep] : "-"));
379 sprintf(pos + strlen(pos), " bm 1; id 1;");
380 }
381
382
SaveEPD(char * p)383 void SaveEPD (char *p)
384 /**************************************************************************
385 *
386 * This routine appends the current position in EPD format into a file.
387 *
388 **************************************************************************/
389 {
390 char file[MAXSTR];
391 FILE *fp;
392 char pos[MAXSTR] = "";
393
394 EPD2str(pos);
395 sscanf (p, "%s ", file);
396 fp = fopen (file, "a");
397 fprintf(fp, "%s\n", pos);
398 fclose (fp);
399 }
400