| 245 default: |
245 default: |
| 246 return 0; |
246 return 0; |
| 247 } |
247 } |
| 248 } |
248 } |
| 249 |
249 |
| 250 static bool is_stalemate(const GameState *gamestate) { |
250 static bool check_stalemate(const GameState *gamestate) { |
| 251 Color next_player = gamestate->movecount % 2 == 0 ? WHITE : BLACK; |
251 Color next_player = gamestate->movecount % 2 == 0 ? WHITE : BLACK; |
| 252 |
252 |
| 253 /* scan the board for pieces of the next player's color */ |
253 /* scan the board for pieces of the next player's color */ |
| 254 Move moves[QUEEN_MOVES_MAX]; |
254 Move moves[QUEEN_MOVES_MAX]; |
| 255 for (Row r = 0; r < 8; r++) { |
255 for (Row r = 0; r < 8; r++) { |
| 260 } |
260 } |
| 261 } |
261 } |
| 262 } |
262 } |
| 263 |
263 |
| 264 return true; |
264 return true; |
| |
265 } |
| |
266 |
| |
267 static size_t fen_len_without_moves(const char *fen) { |
| |
268 size_t len = strlen(fen); |
| |
269 /* find first space that separates the two move counters */ |
| |
270 while (--len > 0 && fen[len] != ' ') {} |
| |
271 /* find second space that separates the move counters from the rest */ |
| |
272 while (--len > 0 && fen[len] != ' ') {} |
| |
273 return len; |
| |
274 } |
| |
275 |
| |
276 bool check_threefold_repetition(const GameState *gamestate) { |
| |
277 if (gamestate->movecount < 3) { |
| |
278 return false; |
| |
279 } |
| |
280 |
| |
281 /* take the part of the FEN that only describes the board */ |
| |
282 const char *fen = gamestate->fen[gamestate->movecount - 1]; |
| |
283 size_t fen_len = fen_len_without_moves(fen); |
| |
284 |
| |
285 // TODO: develop a test case that involves en passant and add support here |
| |
286 |
| |
287 /* count the previous occurrences */ |
| |
288 unsigned c = 0; |
| |
289 for (size_t i = gamestate->movecount - 1; i > 0;) { |
| |
290 const char *other = gamestate->fen[--i]; |
| |
291 size_t other_len = fen_len_without_moves(other); |
| |
292 if (fen_len != other_len) continue; |
| |
293 if (strncmp(fen, other, fen_len) == 0) { |
| |
294 if (++c == 2) return true; |
| |
295 } |
| |
296 } |
| |
297 return false; |
| 265 } |
298 } |
| 266 |
299 |
| 267 char getpiecechr(Piece piece) { |
300 char getpiecechr(Piece piece) { |
| 268 switch (piece_type(piece)) { |
301 switch (piece_type(piece)) { |
| 269 case ROOK: return 'R'; |
302 case ROOK: return 'R'; |
| 308 case 'K': return mkpiece(KING, color); |
341 case 'K': return mkpiece(KING, color); |
| 309 default: return 0; |
342 default: return 0; |
| 310 } |
343 } |
| 311 } |
344 } |
| 312 |
345 |
| |
346 /* applies the move without recalculating gamestate flags */ |
| 313 static void apply_move_internal(GameState *gamestate, Move *move) { |
347 static void apply_move_internal(GameState *gamestate, Move *move) { |
| 314 /* en passant capture */ |
348 /* en passant capture */ |
| 315 if (move->capture && piece_type(move->piece) == PAWN && |
349 if (move->capture && piece_type(move->piece) == PAWN && |
| 316 piece_at(gamestate, mdst(move)) == 0) { |
350 piece_at(gamestate, mdst(move)) == 0) { |
| 317 piece_remove(gamestate, move->fromrow, move->tofile); |
351 piece_remove(gamestate, move->fromrow, move->tofile); |
| 373 calc_movetime(gamestate, melem); |
407 calc_movetime(gamestate, melem); |
| 374 } |
408 } |
| 375 |
409 |
| 376 /* important: only "add" the move after calculating the time! */ |
410 /* important: only "add" the move after calculating the time! */ |
| 377 gamestate->movecount++; |
411 gamestate->movecount++; |
| 378 |
|
| 379 /* did this move checkmate the other king? */ |
|
| 380 gamestate->checkmate = move->checkmate; |
|
| 381 } |
412 } |
| 382 |
413 |
| 383 void apply_move(GameState *gamestate, Move *move) { |
414 void apply_move(GameState *gamestate, Move *move) { |
| 384 apply_move_internal(gamestate, move); |
415 apply_move_internal(gamestate, move); |
| 385 if (!gamestate->checkmate) { |
416 |
| 386 gamestate->stalemate = is_stalemate(gamestate); |
417 /* calculate gamestate flags in order of efficiency */ |
| |
418 if (move->checkmate) { |
| |
419 gamestate->checkmate = true; |
| |
420 } else if (check_threefold_repetition(gamestate)) { |
| |
421 gamestate->threefold = true; |
| |
422 } else if (check_stalemate(gamestate)) { |
| |
423 gamestate->stalemate = true; |
| 387 } |
424 } |
| 388 } |
425 } |
| 389 |
426 |
| 390 void gamestate_at_move(const GameState *gamestate, |
427 void gamestate_at_move(const GameState *gamestate, |
| 391 unsigned move_number, GameState *replay) { |
428 unsigned move_number, GameState *replay) { |
| 1179 } else { |
1216 } else { |
| 1180 return snprintf(str, 6, "%02u:%02u", minutes, seconds); |
1217 return snprintf(str, 6, "%02u:%02u", minutes, seconds); |
| 1181 } |
1218 } |
| 1182 } |
1219 } |
| 1183 |
1220 |
| 1184 bool check_threefold_repetition(const GameState *gamestate) { |
|
| 1185 // TODO: implement threefold repetition detection |
|
| 1186 return false; |
|
| 1187 } |
|
| 1188 |
|
| 1189 size_t filter_moves_allowed(const GameState *gamestate, |
1221 size_t filter_moves_allowed(const GameState *gamestate, |
| 1190 Color c, Row r, File f, Move *moves, moves_generator_func func) { |
1222 Color c, Row r, File f, Move *moves, moves_generator_func func) { |
| 1191 |
1223 |
| 1192 /* worst case: the queen has the most moves */ |
1224 /* worst case: the queen has the most moves */ |
| 1193 Move candidates[QUEEN_MOVES_MAX]; |
1225 Move candidates[QUEEN_MOVES_MAX]; |