src/chess/rules.c

changeset 173
e541b6002933
parent 170
bde99d803caf
equal deleted inserted replaced
172:ba1bf80d10cc 173:e541b6002933
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];

mercurial