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