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