Add forgotten files 1.4.70b
[polyglot.git] / fen.c
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
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
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