src/chess/rules.c

changeset 195
27d02ccb0cef
parent 194
619f07c95894
equal deleted inserted replaced
194:619f07c95894 195:27d02ccb0cef
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];

mercurial