| 124 move->movetime = cur - last; |
124 move->movetime = cur - last; |
| 125 } |
125 } |
| 126 } |
126 } |
| 127 |
127 |
| 128 size_t piece_moves_allowed(const GameState *gamestate, |
128 size_t piece_moves_allowed(const GameState *gamestate, |
| 129 Rank r, File f, Move *moves) { |
129 File f, Rank r, Move *moves) { |
| 130 Piece p = piece_at(gamestate, r, f); |
130 Piece p = piece_at(gamestate, f, r); |
| 131 Color c = piece_color(p); |
131 Color c = piece_color(p); |
| 132 switch (piece_type(p)) { |
132 switch (piece_type(p)) { |
| 133 case KING: |
133 case KING: |
| 134 return king_moves_allowed(gamestate, c, r, f, moves); |
134 return king_moves_allowed(gamestate, c, f, r, moves); |
| 135 case QUEEN: |
135 case QUEEN: |
| 136 return queen_moves_allowed(gamestate, c, r, f, moves); |
136 return queen_moves_allowed(gamestate, c, f, r, moves); |
| 137 case ROOK: |
137 case ROOK: |
| 138 return rook_moves_allowed(gamestate, c, r, f, moves); |
138 return rook_moves_allowed(gamestate, c, f, r, moves); |
| 139 case KNIGHT: |
139 case KNIGHT: |
| 140 return knight_moves_allowed(gamestate, c, r, f, moves); |
140 return knight_moves_allowed(gamestate, c, f, r, moves); |
| 141 case BISHOP: |
141 case BISHOP: |
| 142 return bishop_moves_allowed(gamestate, c, r, f, moves); |
142 return bishop_moves_allowed(gamestate, c, f, r, moves); |
| 143 case PAWN: |
143 case PAWN: |
| 144 return pawn_moves_allowed(gamestate, c, r, f, moves); |
144 return pawn_moves_allowed(gamestate, c, f, r, moves); |
| 145 default: |
145 default: |
| 146 return 0; |
146 return 0; |
| 147 } |
147 } |
| 148 } |
148 } |
| 149 |
149 |
| 152 |
152 |
| 153 /* scan the board for pieces of the next player's color */ |
153 /* scan the board for pieces of the next player's color */ |
| 154 Move moves[QUEEN_MOVES_MAX]; |
154 Move moves[QUEEN_MOVES_MAX]; |
| 155 for (Rank r = 0; r < 8; r++) { |
155 for (Rank r = 0; r < 8; r++) { |
| 156 for (File f = 0; f < 8; f++) { |
156 for (File f = 0; f < 8; f++) { |
| 157 if (piece_color(piece_at(gamestate, r, f)) == next_player |
157 if (piece_color(piece_at(gamestate, f, r)) == next_player |
| 158 && piece_moves_allowed(gamestate, r, f, moves) > 0) { |
158 && piece_moves_allowed(gamestate, f, r, moves) > 0) { |
| 159 return false; |
159 return false; |
| 160 } |
160 } |
| 161 } |
161 } |
| 162 } |
162 } |
| 163 |
163 |
| 203 unsigned op_piece_count[7] = {0}; |
203 unsigned op_piece_count[7] = {0}; |
| 204 bool has_bbishop = false, has_wbishop = false; |
204 bool has_bbishop = false, has_wbishop = false; |
| 205 bool op_has_bbishop = false, op_has_wbishop = false; |
205 bool op_has_bbishop = false, op_has_wbishop = false; |
| 206 for (Rank r = 0 ; r < 8 ; r++) { |
206 for (Rank r = 0 ; r < 8 ; r++) { |
| 207 for (File f = 0 ; f < 8 ; f++) { |
207 for (File f = 0 ; f < 8 ; f++) { |
| 208 Piece p = piece_at(gamestate, r, f); |
208 Piece p = piece_at(gamestate, f, r); |
| 209 if (piece_color(p) == color) { |
209 if (piece_color(p) == color) { |
| 210 piece_count[piece_type(p)]++; |
210 piece_count[piece_type(p)]++; |
| 211 if (piece_type(p) == BISHOP) { |
211 if (piece_type(p) == BISHOP) { |
| 212 if (field_color(r, f) == WHITE) { |
212 if (field_color(f, r) == WHITE) { |
| 213 has_wbishop = true; |
213 has_wbishop = true; |
| 214 } else { |
214 } else { |
| 215 has_bbishop = true; |
215 has_bbishop = true; |
| 216 } |
216 } |
| 217 } |
217 } |
| 218 } else { |
218 } else { |
| 219 op_piece_count[piece_type(p)]++; |
219 op_piece_count[piece_type(p)]++; |
| 220 if (piece_type(p) == BISHOP) { |
220 if (piece_type(p) == BISHOP) { |
| 221 if (field_color(r, f) == WHITE) { |
221 if (field_color(f, r) == WHITE) { |
| 222 op_has_wbishop = true; |
222 op_has_wbishop = true; |
| 223 } else { |
223 } else { |
| 224 op_has_bbishop = true; |
224 op_has_bbishop = true; |
| 225 } |
225 } |
| 226 } |
226 } |
| 318 |
318 |
| 319 /* applies the move without recalculating gamestate flags */ |
319 /* applies the move without recalculating gamestate flags */ |
| 320 static void apply_move_internal(GameState *gamestate, Move *move) { |
320 static void apply_move_internal(GameState *gamestate, Move *move) { |
| 321 /* en passant capture */ |
321 /* en passant capture */ |
| 322 if (move->capture && piece_type(move->piece) == PAWN && |
322 if (move->capture && piece_type(move->piece) == PAWN && |
| 323 piece_at(gamestate, move->torank, move->tofile) == 0) { |
323 piece_at(gamestate, move->tofile, move->torank) == 0) { |
| 324 piece_remove(gamestate, move->fromrank, move->tofile); |
324 piece_remove(gamestate, move->tofile, move->fromrank); |
| 325 } |
325 } |
| 326 |
326 |
| 327 /* remove old en passant threats */ |
327 /* remove old en passant threats */ |
| 328 for (File file = 0 ; file < 8 ; file++) { |
328 for (File file = 0 ; file < 8 ; file++) { |
| 329 enpassant_threat_remove(gamestate, 3, file); |
329 enpassant_threat_remove(gamestate, file, 3); |
| 330 enpassant_threat_remove(gamestate, 4, file); |
330 enpassant_threat_remove(gamestate, file, 4); |
| 331 } |
331 } |
| 332 |
332 |
| 333 /* move (and maybe capture or promote) */ |
333 /* move (and maybe capture or promote) */ |
| 334 piece_remove(gamestate, move->fromrank, move->fromfile); |
334 piece_remove(gamestate, move->fromfile, move->fromrank); |
| 335 if (move->promotion) { |
335 if (move->promotion) { |
| 336 piece_set(gamestate, move->torank, move->tofile, move->promotion); |
336 piece_set(gamestate, move->tofile, move->torank, move->promotion); |
| 337 } else { |
337 } else { |
| 338 piece_set(gamestate, move->torank, move->tofile, move->piece); |
338 piece_set(gamestate, move->tofile, move->torank, move->piece); |
| 339 } |
339 } |
| 340 |
340 |
| 341 /* add new en passant threat */ |
341 /* add new en passant threat */ |
| 342 if (piece_type(move->piece) == PAWN && ( |
342 if (piece_type(move->piece) == PAWN && ( |
| 343 (move->fromrank == 1 && move->torank == 3) || |
343 (move->fromrank == 1 && move->torank == 3) || |
| 344 (move->fromrank == 6 && move->torank == 4))) { |
344 (move->fromrank == 6 && move->torank == 4))) { |
| 345 enpassant_threat_add(gamestate, move->torank, move->tofile); |
345 enpassant_threat_add(gamestate, move->tofile, move->torank); |
| 346 } |
346 } |
| 347 |
347 |
| 348 /* castling */ |
348 /* castling */ |
| 349 if (piece_type(move->piece) == KING && move->fromfile == fileidx('e')) { |
349 if (piece_type(move->piece) == KING && move->fromfile == fileidx('e')) { |
| 350 const Color color = piece_color(move->piece); |
350 const Color color = piece_color(move->piece); |
| 422 Color oppcolor = opponent_color(piececolor); |
422 Color oppcolor = opponent_color(piececolor); |
| 423 File opkingfile = 0; |
423 File opkingfile = 0; |
| 424 Rank opkingrank = 0; |
424 Rank opkingrank = 0; |
| 425 for (Rank rank = 0 ; rank < 8 ; rank++) { |
425 for (Rank rank = 0 ; rank < 8 ; rank++) { |
| 426 for (File file = 0 ; file < 8 ; file++) { |
426 for (File file = 0 ; file < 8 ; file++) { |
| 427 Piece p = piece_at(&simulation, rank, file); |
427 Piece p = piece_at(&simulation, file, rank); |
| 428 if (p == mkpiece(KING, oppcolor)) { |
428 if (p == mkpiece(KING, oppcolor)) { |
| 429 opkingfile = file; |
429 opkingfile = file; |
| 430 opkingrank = rank; |
430 opkingrank = rank; |
| 431 } |
431 } |
| 432 } |
432 } |
| 433 } |
433 } |
| 434 |
434 |
| 435 /* determine if the opposing king is now threatened */ |
435 /* determine if the opposing king is now threatened */ |
| 436 Move threats[16]; |
436 Move threats[16]; |
| 437 size_t threatcount; |
437 size_t threatcount; |
| 438 bool incheck = get_threats(&simulation, opkingrank, opkingfile, |
438 bool incheck = get_threats(&simulation, opkingfile, opkingrank, |
| 439 piececolor, threats, &threatcount); |
439 piececolor, threats, &threatcount); |
| 440 |
440 |
| 441 if (!incheck) { |
441 if (!incheck) { |
| 442 gamestate_cleanup(&simulation); |
442 gamestate_cleanup(&simulation); |
| 443 return 0; |
443 return 0; |
| 455 /* check if piece of the king's color blocks the field */ |
455 /* check if piece of the king's color blocks the field */ |
| 456 if (piece_color(simulation.board[er][ef]) == oppcolor) |
456 if (piece_color(simulation.board[er][ef]) == oppcolor) |
| 457 continue; |
457 continue; |
| 458 |
458 |
| 459 /* check if escape field is already covered (threatened) */ |
459 /* check if escape field is already covered (threatened) */ |
| 460 if (is_covered(&simulation, er, ef, piececolor)) |
460 if (is_covered(&simulation, ef, er, piececolor)) |
| 461 continue; |
461 continue; |
| 462 |
462 |
| 463 /* check if an attacking piece blocks the field */ |
463 /* check if an attacking piece blocks the field */ |
| 464 if (piece_color(simulation.board[er][ef]) == piececolor) { |
464 if (piece_color(simulation.board[er][ef]) == piececolor) { |
| 465 /* test if the king can fight back */ |
465 /* test if the king can fight back */ |
| 470 move_retaliate.fromfile = opkingfile; |
470 move_retaliate.fromfile = opkingfile; |
| 471 move_retaliate.torank = er; |
471 move_retaliate.torank = er; |
| 472 move_retaliate.tofile = ef; |
472 move_retaliate.tofile = ef; |
| 473 move_retaliate.capture = true; |
473 move_retaliate.capture = true; |
| 474 apply_move_internal(&sim_retaliate, &move_retaliate); |
474 apply_move_internal(&sim_retaliate, &move_retaliate); |
| 475 canescape = !is_covered(&sim_retaliate, er, ef, piececolor); |
475 canescape = !is_covered(&sim_retaliate, ef, er, piececolor); |
| 476 gamestate_cleanup(&sim_retaliate); |
476 gamestate_cleanup(&sim_retaliate); |
| 477 continue; |
477 continue; |
| 478 } |
478 } |
| 479 |
479 |
| 480 /* the field is not covered and unoccupied */ |
480 /* the field is not covered and unoccupied */ |
| 483 } |
483 } |
| 484 |
484 |
| 485 /* can't escape, can the king be rescued? */ |
485 /* can't escape, can the king be rescued? */ |
| 486 if (!canescape && threatcount == 1) { |
486 if (!canescape && threatcount == 1) { |
| 487 canescape = is_protected(&simulation, |
487 canescape = is_protected(&simulation, |
| 488 threats[0].fromrank, threats[0].fromfile, oppcolor); |
488 threats[0].fromfile, threats[0].fromrank, oppcolor); |
| 489 } |
489 } |
| 490 |
490 |
| 491 /* can't capture, can he block? */ |
491 /* can't capture, can he block? */ |
| 492 if (!canescape && threatcount == 1) { |
492 if (!canescape && threatcount == 1) { |
| 493 Move *threat = &(threats[0]); |
493 Move *threat = &(threats[0]); |
| 500 int d = threat->tofile > threat->fromfile ? 1 : -1; |
500 int d = threat->tofile > threat->fromfile ? 1 : -1; |
| 501 File file = threat->fromfile; |
501 File file = threat->fromfile; |
| 502 while (!canescape && file != threat->tofile - d) { |
502 while (!canescape && file != threat->tofile - d) { |
| 503 file += d; |
503 file += d; |
| 504 canescape |= is_protected(&simulation, |
504 canescape |= is_protected(&simulation, |
| 505 threat->torank, file, oppcolor); |
505 file, threat->torank, oppcolor); |
| 506 } |
506 } |
| 507 } else if (threat->fromfile == threat->tofile) { |
507 } else if (threat->fromfile == threat->tofile) { |
| 508 /* rook aspect (on file) */ |
508 /* rook aspect (on file) */ |
| 509 int d = threat->torank > threat->fromrank ? 1 : -1; |
509 int d = threat->torank > threat->fromrank ? 1 : -1; |
| 510 Rank rank = threat->fromrank; |
510 Rank rank = threat->fromrank; |
| 511 while (!canescape && rank != threat->torank - d) { |
511 while (!canescape && rank != threat->torank - d) { |
| 512 rank += d; |
512 rank += d; |
| 513 canescape |= is_protected(&simulation, |
513 canescape |= is_protected(&simulation, |
| 514 rank, threat->tofile, oppcolor); |
514 threat->tofile, rank, oppcolor); |
| 515 } |
515 } |
| 516 } else { |
516 } else { |
| 517 /* bishop aspect */ |
517 /* bishop aspect */ |
| 518 int dr = threat->torank > threat->fromrank ? 1 : -1; |
518 int dr = threat->torank > threat->fromrank ? 1 : -1; |
| 519 int df = threat->tofile > threat->fromfile ? 1 : -1; |
519 int df = threat->tofile > threat->fromfile ? 1 : -1; |
| 522 File file = threat->fromfile; |
522 File file = threat->fromfile; |
| 523 while (!canescape && file != threat->tofile - df |
523 while (!canescape && file != threat->tofile - df |
| 524 && rank != threat->torank - dr) { |
524 && rank != threat->torank - dr) { |
| 525 rank += dr; |
525 rank += dr; |
| 526 file += df; |
526 file += df; |
| 527 canescape |= is_protected(&simulation, rank, file, |
527 canescape |= is_protected(&simulation, |
| 528 oppcolor); |
528 file, rank, oppcolor); |
| 529 } |
529 } |
| 530 } |
530 } |
| 531 } |
531 } |
| 532 } |
532 } |
| 533 gamestate_cleanup(&simulation); |
533 gamestate_cleanup(&simulation); |
| 564 } |
564 } |
| 565 } else if (piece_type(move->piece) != KING) { |
565 } else if (piece_type(move->piece) != KING) { |
| 566 /* resolve ambiguities, if any */ |
566 /* resolve ambiguities, if any */ |
| 567 Move candidates[16]; |
567 Move candidates[16]; |
| 568 size_t ccount; |
568 size_t ccount; |
| 569 if (get_real_candidates(gamestate, move->torank, move->tofile, |
569 if (get_real_candidates(gamestate, move->tofile, move->torank, |
| 570 piece_color(move->piece), candidates, &ccount)) { |
570 piece_color(move->piece), candidates, &ccount)) { |
| 571 unsigned int ambranks = 0, ambfiles = 0, ambpiece = 0; |
571 unsigned int ambranks = 0, ambfiles = 0, ambpiece = 0; |
| 572 for (size_t i = 0 ; i < ccount ; i++) { |
572 for (size_t i = 0 ; i < ccount ; i++) { |
| 573 if (candidates[i].piece == move->piece) { |
573 if (candidates[i].piece == move->piece) { |
| 574 ambpiece++; |
574 ambpiece++; |
| 635 if (move->fromfile == move->tofile && move->fromrank == move->torank) { |
635 if (move->fromfile == move->tofile && move->fromrank == move->torank) { |
| 636 return RULES_VIOLATED; |
636 return RULES_VIOLATED; |
| 637 } |
637 } |
| 638 |
638 |
| 639 /* does piece exist */ |
639 /* does piece exist */ |
| 640 if (piece_at(gamestate, move->fromrank, move->fromfile) != move->piece) { |
640 if (piece_at(gamestate, move->fromfile, move->fromrank) != move->piece) { |
| 641 return PIECE_NOT_FOUND; |
641 return PIECE_NOT_FOUND; |
| 642 } |
642 } |
| 643 |
643 |
| 644 /* is there any piece at the destination? */ |
644 /* is there any piece at the destination? */ |
| 645 Piece piece_at_dst = piece_at(gamestate, move->torank, move->tofile); |
645 Piece piece_at_dst = piece_at(gamestate, move->tofile, move->torank); |
| 646 |
646 |
| 647 /* can't capture own pieces */ |
647 /* can't capture own pieces */ |
| 648 if (piece_color(piece_at_dst) == piece_color(move->piece)) { |
648 if (piece_color(piece_at_dst) == piece_color(move->piece)) { |
| 649 return RULES_VIOLATED; |
649 return RULES_VIOLATED; |
| 650 } |
650 } |
| 652 /* must capture, if and only if destination is occupied... */ |
652 /* must capture, if and only if destination is occupied... */ |
| 653 if (!((piece_at_dst == 0) ^ move->capture)) { |
653 if (!((piece_at_dst == 0) ^ move->capture)) { |
| 654 /* ... or the capture happens en passant */ |
654 /* ... or the capture happens en passant */ |
| 655 if (!move->capture || piece_type(move->piece) != PAWN || |
655 if (!move->capture || piece_type(move->piece) != PAWN || |
| 656 !enpassant_threat_exists(gamestate, |
656 !enpassant_threat_exists(gamestate, |
| 657 move->fromrank, move->tofile)) { |
657 move->tofile, move->fromrank)) { |
| 658 return RULES_VIOLATED; |
658 return RULES_VIOLATED; |
| 659 } |
659 } |
| 660 } |
660 } |
| 661 |
661 |
| 662 /* validate individual rules */ |
662 /* validate individual rules */ |
| 707 Color oppcolor = opponent_color(piececolor); |
707 Color oppcolor = opponent_color(piececolor); |
| 708 File kingfile = 0; |
708 File kingfile = 0; |
| 709 Rank kingrank = 0; |
709 Rank kingrank = 0; |
| 710 for (Rank rank = 0 ; rank < 8 ; rank++) { |
710 for (Rank rank = 0 ; rank < 8 ; rank++) { |
| 711 for (File file = 0 ; file < 8 ; file++) { |
711 for (File file = 0 ; file < 8 ; file++) { |
| 712 Piece p = piece_at(&simulation, rank, file); |
712 Piece p = piece_at(&simulation, file, rank); |
| 713 if (p == mkpiece(KING, piececolor)) { |
713 if (p == mkpiece(KING, piececolor)) { |
| 714 kingfile = file; |
714 kingfile = file; |
| 715 kingrank = rank; |
715 kingrank = rank; |
| 716 } |
716 } |
| 717 } |
717 } |
| 718 } |
718 } |
| 719 if (is_covered(&simulation, kingrank, kingfile, oppcolor)) { |
719 if (is_covered(&simulation, kingfile, kingrank, oppcolor)) { |
| 720 if (piece_type(move->piece) == KING) { |
720 if (piece_type(move->piece) == KING) { |
| 721 result = KING_MOVES_INTO_CHECK; |
721 result = KING_MOVES_INTO_CHECK; |
| 722 } else { |
722 } else { |
| 723 if (is_check_position(gamestate)) { |
723 if (is_check_position(gamestate)) { |
| 724 result = KING_IN_CHECK; |
724 result = KING_IN_CHECK; |
| 750 } |
750 } |
| 751 |
751 |
| 752 return VALID_MOVE_SEMANTICS; |
752 return VALID_MOVE_SEMANTICS; |
| 753 } |
753 } |
| 754 |
754 |
| 755 Piece piece_at(const GameState *gamestate, Rank rank, File file) { |
755 Piece piece_at(const GameState *gamestate, File file, Rank rank) { |
| 756 return gamestate->board[rank][file] & (PIECE_MASK|COLOR_MASK); |
756 return gamestate->board[rank][file] & (PIECE_MASK|COLOR_MASK); |
| 757 } |
757 } |
| 758 |
758 |
| 759 void piece_set(GameState *gamestate, Rank rank, File file, Piece piece) { |
759 void piece_set(GameState *gamestate, File file, Rank rank, Piece piece) { |
| 760 gamestate->board[rank][file] = piece; |
760 gamestate->board[rank][file] = piece; |
| 761 } |
761 } |
| 762 |
762 |
| 763 bool get_candidates(const GameState *gamestate, Rank rank, File file, |
763 bool get_candidates(const GameState *gamestate, File file, Rank rank, |
| 764 Color color, Move *moves, size_t *movecount) { |
764 Color color, Move *moves, size_t *movecount) { |
| 765 Move candidates[32]; |
765 Move candidates[32]; |
| 766 size_t ccount = 0; |
766 size_t ccount = 0; |
| 767 for (Rank r = 0 ; r < 8 ; r++) { |
767 for (Rank r = 0 ; r < 8 ; r++) { |
| 768 for (File f = 0 ; f < 8 ; f++) { |
768 for (File f = 0 ; f < 8 ; f++) { |
| 769 Piece p = piece_at(gamestate, r, f); |
769 Piece p = piece_at(gamestate, f, r); |
| 770 if (piece_color(p) == color) { |
770 if (piece_color(p) == color) { |
| 771 /* non-capturing move */ |
771 /* non-capturing move */ |
| 772 memset(&(candidates[ccount]), 0, sizeof(Move)); |
772 memset(&(candidates[ccount]), 0, sizeof(Move)); |
| 773 candidates[ccount].piece = p; |
773 candidates[ccount].piece = p; |
| 774 candidates[ccount].fromrank = r; |
774 candidates[ccount].fromrank = r; |
| 807 } |
807 } |
| 808 |
808 |
| 809 return result; |
809 return result; |
| 810 } |
810 } |
| 811 |
811 |
| 812 bool get_threats(const GameState *gamestate, Rank rank, File file, |
812 bool get_threats(const GameState *gamestate, File file, Rank rank, |
| 813 Color color, Move *threats, size_t *threatcount) { |
813 Color color, Move *threats, size_t *threatcount) { |
| 814 |
814 |
| 815 /* simulate a capturing move on the target position */ |
815 /* simulate a capturing move on the target position */ |
| 816 Color opcolor = opponent_color(color); |
816 Color opcolor = opponent_color(color); |
| 817 GameState simulation = gamestate_copy_sim(gamestate); |
817 GameState simulation = gamestate_copy_sim(gamestate); |
| 818 if (piece_color(piece_at(&simulation, rank, file)) != opcolor) { |
818 if (piece_color(piece_at(&simulation, file, rank)) != opcolor) { |
| 819 /* set a fake pawn if the field is not occupied by the opponent */ |
819 /* set a fake pawn if the field is not occupied by the opponent */ |
| 820 piece_set(&simulation, rank, file, mkpiece(PAWN, opcolor)); |
820 piece_set(&simulation, file, rank, mkpiece(PAWN, opcolor)); |
| 821 } |
821 } |
| 822 |
822 |
| 823 Move candidates[16]; |
823 Move candidates[16]; |
| 824 size_t ccount = 0; |
824 size_t ccount = 0; |
| 825 for (Rank r = 0 ; r < 8 ; r++) { |
825 for (Rank r = 0 ; r < 8 ; r++) { |
| 826 for (File f = 0 ; f < 8 ; f++) { |
826 for (File f = 0 ; f < 8 ; f++) { |
| 827 Piece p = piece_at(&simulation, r, f); |
827 Piece p = piece_at(&simulation, f, r); |
| 828 if (piece_color(p) == color) { |
828 if (piece_color(p) == color) { |
| 829 memset(&(candidates[ccount]), 0, sizeof(Move)); |
829 memset(&(candidates[ccount]), 0, sizeof(Move)); |
| 830 candidates[ccount].piece = p; |
830 candidates[ccount].piece = p; |
| 831 candidates[ccount].fromrank = r; |
831 candidates[ccount].fromrank = r; |
| 832 candidates[ccount].fromfile = f; |
832 candidates[ccount].fromfile = f; |
| 872 |
872 |
| 873 File kingfile = 0; |
873 File kingfile = 0; |
| 874 Rank kingrank = 0; |
874 Rank kingrank = 0; |
| 875 for (Rank rank = 0 ; rank < 8 ; rank++) { |
875 for (Rank rank = 0 ; rank < 8 ; rank++) { |
| 876 for (File file = 0 ; file < 8 ; file++) { |
876 for (File file = 0 ; file < 8 ; file++) { |
| 877 if (piece_at(&simulation, rank, file) == mkpiece(KING, color)) { |
877 if (piece_at(&simulation, file, rank) == mkpiece(KING, color)) { |
| 878 kingfile = file; |
878 kingfile = file; |
| 879 kingrank = rank; |
879 kingrank = rank; |
| 880 } |
880 } |
| 881 } |
881 } |
| 882 } |
882 } |
| 883 |
883 |
| 884 bool covered = is_covered(&simulation, |
884 bool covered = is_covered(&simulation, |
| 885 kingrank, kingfile, opponent_color(color)); |
885 kingfile, kingrank, opponent_color(color)); |
| 886 gamestate_cleanup(&simulation); |
886 gamestate_cleanup(&simulation); |
| 887 |
887 |
| 888 return covered; |
888 return covered; |
| 889 } |
889 } |
| 890 |
890 |
| 891 bool get_real_candidates(const GameState *gamestate, Rank rank, File file, |
891 bool get_real_candidates(const GameState *gamestate, File file, Rank rank, |
| 892 Color color, Move *moves, size_t *movecount) { |
892 Color color, Move *moves, size_t *movecount) { |
| 893 |
893 |
| 894 if (movecount) { |
894 if (movecount) { |
| 895 *movecount = 0; |
895 *movecount = 0; |
| 896 } |
896 } |
| 897 |
897 |
| 898 Move candidates[16]; |
898 Move candidates[16]; |
| 899 size_t ccount; |
899 size_t ccount; |
| 900 if (get_candidates(gamestate, rank, file, color, candidates, &ccount)) { |
900 if (get_candidates(gamestate, file, rank, color, candidates, &ccount)) { |
| 901 bool result = false; |
901 bool result = false; |
| 902 for (size_t i = 0 ; i < ccount ; i++) { |
902 for (size_t i = 0 ; i < ccount ; i++) { |
| 903 if (!is_pinned(gamestate, &candidates[i])) { |
903 if (!is_pinned(gamestate, &candidates[i])) { |
| 904 result = true; |
904 result = true; |
| 905 if (moves && movecount) { |
905 if (moves && movecount) { |
| 911 } else { |
911 } else { |
| 912 return false; |
912 return false; |
| 913 } |
913 } |
| 914 } |
914 } |
| 915 |
915 |
| 916 bool get_real_threats(const GameState *gamestate, Rank rank, File file, |
916 bool get_real_threats(const GameState *gamestate, File file, Rank rank, |
| 917 Color color, Move *threats, size_t *threatcount) { |
917 Color color, Move *threats, size_t *threatcount) { |
| 918 |
918 |
| 919 if (threatcount) { |
919 if (threatcount) { |
| 920 *threatcount = 0; |
920 *threatcount = 0; |
| 921 } |
921 } |
| 922 |
922 |
| 923 Move candidates[16]; |
923 Move candidates[16]; |
| 924 size_t ccount; |
924 size_t ccount; |
| 925 if (get_threats(gamestate, rank, file, color, candidates, &ccount)) { |
925 if (get_threats(gamestate, file, rank, color, candidates, &ccount)) { |
| 926 bool result = false; |
926 bool result = false; |
| 927 for (size_t i = 0 ; i < ccount ; i++) { |
927 for (size_t i = 0 ; i < ccount ; i++) { |
| 928 if (!is_pinned(gamestate, &candidates[i])) { |
928 if (!is_pinned(gamestate, &candidates[i])) { |
| 929 result = true; |
929 result = true; |
| 930 if (threats && threatcount) { |
930 if (threats && threatcount) { |
| 948 |
948 |
| 949 Move candidates[16], *candidate = NULL; |
949 Move candidates[16], *candidate = NULL; |
| 950 size_t candidatecount; |
950 size_t candidatecount; |
| 951 |
951 |
| 952 /* determine all candidate moves and sort out the invalid ones */ |
952 /* determine all candidate moves and sort out the invalid ones */ |
| 953 if (get_candidates(gamestate, move->torank, move->tofile, color, |
953 if (get_candidates(gamestate, move->tofile, move->torank, color, |
| 954 candidates, &candidatecount)) { |
954 candidates, &candidatecount)) { |
| 955 |
955 |
| 956 bool found = false; |
956 bool found = false; |
| 957 |
957 |
| 958 for (size_t i = 0 ; i < candidatecount ; i++) { |
958 for (size_t i = 0 ; i < candidatecount ; i++) { |
| 1184 int check_move(const char *mstr, Color color) { |
1184 int check_move(const char *mstr, Color color) { |
| 1185 Move move; |
1185 Move move; |
| 1186 return eval_move1(mstr, &move, color); |
1186 return eval_move1(mstr, &move, color); |
| 1187 } |
1187 } |
| 1188 |
1188 |
| 1189 bool is_protected(const GameState *gamestate, Rank rank, File file, Color color) { |
1189 bool is_protected(const GameState *gamestate, |
| |
1190 File file, Rank rank, Color color) { |
| 1190 Move candidates[16]; |
1191 Move candidates[16]; |
| 1191 size_t ccount; |
1192 size_t ccount; |
| 1192 /* we need all candidates - not only threats! */ |
1193 /* we need all candidates - not only threats! */ |
| 1193 if (get_candidates(gamestate, rank, file, color, candidates, &ccount)) { |
1194 if (get_candidates(gamestate, file, rank, color, candidates, &ccount)) { |
| 1194 for (size_t i = 0 ; i < ccount ; i++) { |
1195 for (size_t i = 0 ; i < ccount ; i++) { |
| 1195 /* skip the king */ |
1196 /* skip the king */ |
| 1196 if (piece_type(candidates[i].piece) == KING) continue; |
1197 if (piece_type(candidates[i].piece) == KING) continue; |
| 1197 /* skip pinned pieces */ |
1198 /* skip pinned pieces */ |
| 1198 if (is_pinned(gamestate, &candidates[i])) continue; |
1199 if (is_pinned(gamestate, &candidates[i])) continue; |
| 1271 return snprintf(str, 6, "%02u:%02u", minutes, seconds); |
1272 return snprintf(str, 6, "%02u:%02u", minutes, seconds); |
| 1272 } |
1273 } |
| 1273 } |
1274 } |
| 1274 |
1275 |
| 1275 size_t filter_moves_allowed(const GameState *gamestate, |
1276 size_t filter_moves_allowed(const GameState *gamestate, |
| 1276 Color c, Rank r, File f, Move *moves, moves_generator_func func) { |
1277 Color c, File f, Rank r, Move *moves, moves_generator_func func) { |
| 1277 |
1278 |
| 1278 /* worst case: the queen has the most moves */ |
1279 /* worst case: the queen has the most moves */ |
| 1279 Move candidates[QUEEN_MOVES_MAX]; |
1280 Move candidates[QUEEN_MOVES_MAX]; |
| 1280 size_t candidatecount = func(gamestate, c, r, f, candidates); |
1281 size_t candidatecount = func(gamestate, c, f, r, candidates); |
| 1281 size_t count = 0; |
1282 size_t count = 0; |
| 1282 |
1283 |
| 1283 for (size_t i = 0 ; i < candidatecount ; i++) { |
1284 for (size_t i = 0 ; i < candidatecount ; i++) { |
| 1284 if (validate_move(gamestate, &candidates[i]) == VALID_MOVE_SEMANTICS) { |
1285 if (validate_move(gamestate, &candidates[i]) == VALID_MOVE_SEMANTICS) { |
| 1285 moves[count++] = candidates[i]; |
1286 moves[count++] = candidates[i]; |