1 
2 // fen.c
3 
4 // includes
5 
6 #include <ctype.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 
10 #include "board.h"
11 #include "colour.h"
12 #include "fen.h"
13 #include "option.h"
14 #include "piece.h"
15 #include "square.h"
16 #include "util.h"
17 
18 // "constants"
19 
20 // const char * StartFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w HAha - 0 1";
21 const char * StartFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
22 
23 // variables
24 
25 static const bool Strict = FALSE;
26 
27 // macros
28 
29 #define skip_white_space() \
30         c=string[pos];\
31         if (c != ' ' && c!='\t') my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos); \
32         while(c==' ' || c=='\t') c=string[++pos];
33 
34 
35 // functions
36 
37 // board_from_fen()
38 
board_from_fen(board_t * board,const char string[])39 bool board_from_fen(board_t * board, const char string[]) {
40 
41    int pos;
42    int file, rank, sq;
43    int c;
44    int i, len;
45    int piece;
46    int king_pos[ColourNb];
47 
48    ASSERT(board!=NULL);
49    ASSERT(string!=NULL);
50 
51    board_clear(board);
52 
53    king_pos[White] = SquareNone;
54    king_pos[Black] = SquareNone;
55 
56    pos = 0;
57    c = string[pos];
58 
59    // piece placement
60 
61    for (rank = 7; rank >= 0; rank--) {
62 
63       for (file = 0; file < 8;) {
64 
65          sq = square_make(file,rank);
66 
67          if (c >= '1' && c <= '8') { // empty square(s)
68 
69             len = c - '0';
70             if (file + len > 8) my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);
71 
72             for (i = 0; i < len; i++) {
73                board->square[sq++] = Empty;
74                file++;
75             }
76 
77          } else { // piece
78 
79             piece = piece_from_char(c);
80             if (piece == PieceNone256) my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);
81 
82             if (piece_is_king(piece)) king_pos[piece_colour(piece)] = sq;
83 
84             board->square[sq++] = piece;
85             file++;
86          }
87 
88          c = string[++pos];
89       }
90 
91       if (rank > 0) {
92          if (c != '/') my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);
93          c = string[++pos];
94      }
95    }
96 
97    // active colour
98 
99    skip_white_space();
100 
101    switch (c) {
102    case 'w':
103       board->turn = White;
104       break;
105    case 'b':
106       board->turn = Black;
107       break;
108    default:
109       my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);
110       break;
111    }
112 
113    c = string[++pos];
114 
115    // castling
116 
117    skip_white_space();
118 
119    board->castle[White][SideH] = SquareNone;
120    board->castle[White][SideA] = SquareNone;
121    board->castle[Black][SideH] = SquareNone;
122    board->castle[Black][SideA] = SquareNone;
123 
124    if (c == '-') { // no castling rights
125 
126       c = string[++pos];
127 
128    } else {
129 
130       // TODO: filter out illegal rights
131 
132       do {
133 
134          if (FALSE) {
135 
136          } else if (c == 'K') {
137 
138             for (sq = H1; sq > king_pos[White]; sq--) {
139                if (board->square[sq] == WhiteRook256) {
140                   board->castle[White][SideH] = sq;
141                   break;
142                }
143             }
144 
145          } else if (c == 'Q') {
146 
147             for (sq = A1; sq < king_pos[White]; sq++) {
148                if (board->square[sq] == WhiteRook256) {
149                   board->castle[White][SideA] = sq;
150                   break;
151                }
152             }
153 
154          } else if (c == 'k') {
155 
156             for (sq = H8; sq > king_pos[Black]; sq--) {
157                if (board->square[sq] == BlackRook256) {
158                   board->castle[Black][SideH] = sq;
159                   break;
160                }
161             }
162 
163          } else if (c == 'q') {
164 
165             for (sq = A8; sq < king_pos[Black]; sq++) {
166                if (board->square[sq] == BlackRook256) {
167                   board->castle[Black][SideA] = sq;
168                   break;
169                }
170             }
171 
172          } else if (c >= 'A' && c <= 'H') {
173 
174             // white castling right
175 
176             sq = square_make(file_from_char(tolower(c)),Rank1);
177 
178             if (sq > king_pos[White]) { // h side
179                board->castle[White][SideH] = sq;
180             } else { // a side
181                board->castle[White][SideA] = sq;
182             }
183 
184          } else if (c >= 'a' && c <= 'h') {
185 
186             // black castling right
187 
188             sq = square_make(file_from_char(tolower(c)),Rank8);
189 
190             if (sq > king_pos[Black]) { // h side
191                board->castle[Black][SideH] = sq;
192             } else { // a side
193                board->castle[Black][SideA] = sq;
194             }
195 
196          } else {
197 
198             my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);
199          }
200 
201          c = string[++pos];
202 
203       } while (c != ' ');
204    }
205 
206    // en-passant
207 
208    skip_white_space();
209 
210    if (c == '-') { // no en-passant
211 
212       sq = SquareNone;
213       c = string[++pos];
214 
215    } else {
216 
217       if (c < 'a' || c > 'h') my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);
218       file = file_from_char(c);
219       c = string[++pos];
220 
221       if (c < '1' || c > '8') my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);
222       rank = rank_from_char(c);
223       c = string[++pos];
224 
225       sq = square_make(file,rank);
226    }
227 
228    board->ep_square = sq;
229 
230    // halfmove clock
231 
232    board->ply_nb = 0;
233    board->move_nb = 0; // HACK, in case of broken syntax
234 
235    if (c != ' ') {
236       if (!Strict) goto update;
237       my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);
238    }
239    c = string[++pos];
240 
241    if (!isdigit(c)) {
242       if (!Strict) goto update;
243       my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);
244    }
245 
246    board->ply_nb = atoi(&string[pos]);
247    do c = string[++pos]; while (isdigit(c));
248 
249    // fullmove number
250 
251    board->move_nb = 0;
252 
253    if (c != ' ') {
254       if (!Strict) goto update;
255       my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);
256    }
257    c = string[++pos];
258 
259    if (!isdigit(c)) {
260       if (!Strict) goto update;
261       my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);
262    }
263 
264    board->move_nb = atoi(&string[pos]) - 1;
265    do c = string[++pos]; while (isdigit(c));
266 
267    // board update
268 
269 update:
270    board_init_list(board);
271 
272    return TRUE;
273 }
274 
275 // board_to_fen()
276 
board_to_fen(const board_t * board,char string[],int size)277 bool board_to_fen(const board_t * board, char string[], int size) {
278 
279    int pos;
280    int file, rank;
281    int sq, piece;
282    int c;
283    int len;
284    int old_pos;
285 
286    ASSERT(board_is_ok(board));
287    ASSERT(string!=NULL);
288    ASSERT(size>=92);
289 
290    // init
291 
292    if (size < 92) return FALSE;
293 
294    pos = 0;
295 
296    // piece placement
297 
298    for (rank = 7; rank >= 0; rank--) {
299 
300       for (file = 0; file < 8;) {
301 
302          sq = square_make(file,rank);
303          piece = board->square[sq];
304          ASSERT(piece==Empty||piece_is_ok(piece));
305 
306          if (piece == Empty) {
307 
308             len = 0;
309             for (; file < 8 && board->square[square_make(file,rank)] == Empty; file++) {
310                len++;
311             }
312 
313             ASSERT(len>=1&&len<=8);
314             c = '0' + len;
315 
316          } else {
317 
318             c = piece_to_char(piece);
319             file++;
320          }
321 
322          string[pos++] = c;
323       }
324 
325       string[pos++] = '/';
326    }
327 
328    string[pos-1] = ' '; // HACK: remove the last '/'
329 
330    // active colour
331 
332    string[pos++] = (colour_is_white(board->turn)) ? 'w' : 'b';
333    string[pos++] = ' ';
334 
335    // castling
336 
337    old_pos = pos;
338 
339    if (option_get_bool(Option,"Chess960")) {
340 
341       // FEN-960
342 
343       if (board->castle[White][SideH] != SquareNone) {
344          string[pos++] = toupper(file_to_char(square_file(board->castle[White][SideH])));
345       }
346 
347       if (board->castle[White][SideA] != SquareNone) {
348          string[pos++] = toupper(file_to_char(square_file(board->castle[White][SideA])));
349       }
350 
351       if (board->castle[Black][SideH] != SquareNone) {
352          string[pos++] = tolower(file_to_char(square_file(board->castle[Black][SideH])));
353       }
354 
355       if (board->castle[Black][SideA] != SquareNone) {
356          string[pos++] = tolower(file_to_char(square_file(board->castle[Black][SideA])));
357       }
358 
359    } else {
360 
361       // FEN
362 
363       if (board->castle[White][SideH] != SquareNone) string[pos++] = 'K';
364       if (board->castle[White][SideA] != SquareNone) string[pos++] = 'Q';
365       if (board->castle[Black][SideH] != SquareNone) string[pos++] = 'k';
366       if (board->castle[Black][SideA] != SquareNone) string[pos++] = 'q';
367    }
368 
369    if (pos == old_pos) string[pos++] = '-';
370 
371    string[pos++] = ' ';
372 
373    // en-passant
374 
375    if (board->ep_square == SquareNone) {
376       string[pos++] = '-';
377    } else {
378       if (!square_to_string(board->ep_square,&string[pos],3)) return FALSE;
379       pos += 2;
380    }
381 
382    string[pos++] = ' ';
383 
384    // halfmove clock and fullmove number
385 
386    sprintf(&string[pos],"%d %d",board->ply_nb,board->move_nb+1);
387 
388    return TRUE;
389 }
390 
391 // end of fen.cpp
392 
393