src/chess/fen.c

changeset 218
1e9751f8eb0d
parent 216
d0c3d3016650
equal deleted inserted replaced
217:507490519676 218:1e9751f8eb0d
29 29
30 #include "fen.h" 30 #include "fen.h"
31 31
32 #include <stdlib.h> 32 #include <stdlib.h>
33 #include <stdio.h> 33 #include <stdio.h>
34 34 #include <string.h>
35 static size_t fen_pieces(char *str, GameState *gamestate) { 35
36 static size_t fen_pieces(char *str, const GameState *gamestate) {
36 size_t i = 0; 37 size_t i = 0;
37 Rank rank = 7; 38 Rank rank = 7;
38 do { 39 do {
39 unsigned int skip = 0; 40 unsigned int skip = 0;
40 for (File file = 0 ; file < 8 ; file++) { 41 for (File file = 0 ; file < 8 ; file++) {
70 } while (rank-- > 0); 71 } while (rank-- > 0);
71 72
72 return i; 73 return i;
73 } 74 }
74 75
75 static size_t fen_color(char *str, GameState *gamestate) { 76 static size_t fen_color(char *str, const GameState *gamestate) {
76 str[0] = current_color(gamestate) == WHITE ? 'w' : 'b'; 77 str[0] = current_color(gamestate) == WHITE ? 'w' : 'b';
77 return 1; 78 return 1;
78 } 79 }
79 80
80 static size_t fen_castling(char *str, GameState *gamestate) { 81 static size_t fen_castling(char *str, const GameState *gamestate) {
81 size_t i = 0; 82 size_t i = 0;
82 if (!gamestate->castling.K) str[i++] = 'K'; 83 if (!gamestate->castling.K) str[i++] = 'K';
83 if (!gamestate->castling.Q) str[i++] = 'Q'; 84 if (!gamestate->castling.Q) str[i++] = 'Q';
84 if (!gamestate->castling.k) str[i++] = 'k'; 85 if (!gamestate->castling.k) str[i++] = 'k';
85 if (!gamestate->castling.q) str[i++] = 'q'; 86 if (!gamestate->castling.q) str[i++] = 'q';
86 if (!i) str[i++] = '-'; 87 if (!i) str[i++] = '-';
87 88
88 return i; 89 return i;
89 } 90 }
90 91
91 static size_t fen_enpassant(char *str, GameState *gamestate) { 92 static size_t fen_enpassant(char *str, const GameState *gamestate) {
92 93
93 str[0] = '-'; 94 str[0] = '-';
94 95
95 for (int file = 0 ; file < 8 ; file++) { 96 for (int file = 0 ; file < 8 ; file++) {
96 if (enpassant_threat_exists(gamestate, file, 3)) { 97 if (enpassant_threat_exists(gamestate, file, 3)) {
104 } 105 }
105 106
106 return str[0] == '-' ? 1 : 2; 107 return str[0] == '-' ? 1 : 2;
107 } 108 }
108 109
109 static size_t fen_halfmove(char *str, GameState *gamestate) { 110 static size_t fen_halfmove(char *str, const GameState *gamestate) {
111 // TODO: respect a possible fifty_ctr_start
110 unsigned int hm = 0; 112 unsigned int hm = 0;
111 for (unsigned int i = 0; i < gamestate->movecount; i++) { 113 for (unsigned int i = 0; i < gamestate->movecount; i++) {
112 if (gamestate->moves[i].capture 114 if (gamestate->moves[i].capture
113 || piece_type(gamestate->moves[i].piece) == PAWN) { 115 || piece_type(gamestate->moves[i].piece) == PAWN) {
114 hm = 0; 116 hm = 0;
118 } 120 }
119 121
120 return sprintf(str, "%u", hm); 122 return sprintf(str, "%u", hm);
121 } 123 }
122 124
123 static size_t fen_movenr(char *str, GameState *gamestate) { 125 static size_t fen_movenr(char *str, const GameState *gamestate) {
124 return sprintf(str, "%u", 1 + gamestate->movecount / 2); 126 unsigned mc = gamestate->movecount + gamestate->move_start;
127 return sprintf(str, "%u", 1 + mc / 2);
125 } 128 }
126 129
127 static size_t fen_space(char *str) { 130 static size_t fen_space(char *str) {
128 *str = ' '; 131 *str = ' ';
129 return 1; 132 return 1;
130 } 133 }
131 134
132 void fen_compute(char *str, GameState *gamestate) { 135 void fen_compute(char *str, const GameState *gamestate) {
133 str += fen_pieces(str, gamestate); 136 str += fen_pieces(str, gamestate);
134 str += fen_space(str); 137 str += fen_space(str);
135 str += fen_color(str, gamestate); 138 str += fen_color(str, gamestate);
136 str += fen_space(str); 139 str += fen_space(str);
137 str += fen_castling(str, gamestate); 140 str += fen_castling(str, gamestate);
141 str += fen_halfmove(str, gamestate); 144 str += fen_halfmove(str, gamestate);
142 str += fen_space(str); 145 str += fen_space(str);
143 str += fen_movenr(str, gamestate); 146 str += fen_movenr(str, gamestate);
144 *str = '\0'; 147 *str = '\0';
145 } 148 }
149
150 static unsigned fen_parse_number(const char *str, unsigned *target) {
151 unsigned l = 0;
152 *target = 0;
153 while (str[l] >= '0' && str[l] <= '9') {
154 unsigned n = str[l] - '0';
155 *target *= 10;
156 *target += n;
157 l++;
158 }
159 /* safety precaution - reject unreasonable high numbers */
160 if (l > 5) return 0;
161 return l;
162 }
163
164 int fen_parse(const char *str, GameState *gamestate) {
165 const char * const fen_start = str;
166 // TODO: think about error reporting that is as good as for PGNs
167
168 if (str == NULL) return 1;
169
170 /* zero-initialize the game state */
171 memset(gamestate, 0, sizeof(GameState));
172
173 /* parse the board (FEN starts top-left at "a8") */
174 Rank r = 7;
175 File f = 0;
176 while (true) {
177 switch (*str) {
178 case 'K': gamestate->board[r][f] = WKING; break;
179 case 'Q': gamestate->board[r][f] = WQUEEN; break;
180 case 'B': gamestate->board[r][f] = WBISHOP; break;
181 case 'N': gamestate->board[r][f] = WKNIGHT; break;
182 case 'R': gamestate->board[r][f] = WROOK; break;
183 case 'P': gamestate->board[r][f] = WPAWN; break;
184 case 'k': gamestate->board[r][f] = BKING; break;
185 case 'q': gamestate->board[r][f] = BQUEEN; break;
186 case 'b': gamestate->board[r][f] = BBISHOP; break;
187 case 'n': gamestate->board[r][f] = BKNIGHT; break;
188 case 'r': gamestate->board[r][f] = BROOK; break;
189 case 'p': gamestate->board[r][f] = BPAWN; break;
190 case '1': break;
191 case '2': f += 1; break;
192 case '3': f += 2; break;
193 case '4': f += 3; break;
194 case '5': f += 4; break;
195 case '6': f += 5; break;
196 case '7': f += 6; break;
197 case '8': f += 7; break;
198 default: return 1;
199 }
200 f++;
201 str++;
202 if (f == 8) {
203 /* rank complete - test for separator or ending space */
204 if (r > 0) {
205 if (*str != '/') return 1;
206 str++;
207 f = 0;
208 r--;
209 } else {
210 if (*str != ' ') return 1;
211 str++;
212 break;
213 }
214 }
215 }
216
217 /* whose turn is it? */
218 bool white_to_move;
219 if (str[0] == 'w') {
220 white_to_move = true;
221 } else if (str[0] == 'b') {
222 white_to_move = false;
223 } else {
224 return 1;
225 }
226 if (str[1] != ' ') return 1;
227 str += 2;
228
229 /* castling rights */
230 gamestate->castling.K = gamestate->castling.Q = true;
231 gamestate->castling.k = gamestate->castling.q = true;
232 if (*str == '-') {
233 str++;
234 } else {
235 char cstl[5] = "KQkq";
236 bool found = false;
237 for (unsigned i = 0 ; i < 4 ; i++) {
238 if (*str == cstl[i]) {
239 found = true;
240 switch (i) {
241 case 0: gamestate->castling.K = false; break;
242 case 1: gamestate->castling.Q = false; break;
243 case 2: gamestate->castling.k = false; break;
244 case 3: gamestate->castling.q = false; break;
245 }
246 str++;
247 }
248 }
249 if (!found) return 1; /* no castling info found */
250 }
251 if (*str != ' ') return 1;
252 str++;
253
254 /* is there an en-passant threat? */
255 if (*str == '-') {
256 str++;
257 } else {
258 if (isfile(str[0]) && isrank(str[1])) {
259 f = fileidx(str[0]);
260 r = rankidx(str[1]);
261 if (r == 2) {
262 r = 3;
263 } else if (r == 5) {
264 r = 4;
265 } else {
266 return 1;
267 }
268 /* the threat is applied to the pawn, not the field it passed */
269 enpassant_threat_add(gamestate, f, r);
270 } else {
271 return 1;
272 }
273 }
274 if (*str != ' ') return 1;
275 str++;
276
277 /* fifty-moves counter */
278 unsigned mnr;
279 unsigned mlen;
280 mlen = fen_parse_number(str, &mnr);
281 if (mlen == 0) return 1;
282 if (str[mlen] != ' ') return 1;
283 str += mlen+1;
284 gamestate->fifty_cntr_start = mnr;
285
286 /* move number */
287 mlen = fen_parse_number(str, &mnr);
288 if (mlen == 0) return 1;
289 if (mnr == 0) return 1;
290 if (str[mlen] != '\0') return 1;
291 str += mlen+1;
292 gamestate->move_start = 2*mnr - 1;
293 if (white_to_move) gamestate->move_start--;
294
295 /* only copy the fen string if everything is a success */
296 gamestate->fen_start = strdup(fen_start);
297 return 0;
298 }

mercurial