src/chess/rules.c

changeset 194
619f07c95894
parent 193
d1420f5c5704
child 195
27d02ccb0cef
equal deleted inserted replaced
193:d1420f5c5704 194:619f07c95894
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 Row r, File f, Move *moves) { 129 Rank r, File f, Move *moves) {
130 Piece p = piece_at(gamestate, r, f); 130 Piece p = piece_at(gamestate, r, f);
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, r, f, moves);
150 static bool check_stalemate(const GameState *gamestate) { 150 static bool check_stalemate(const GameState *gamestate) {
151 Color next_player = gamestate->movecount % 2 == 0 ? WHITE : BLACK; 151 Color next_player = gamestate->movecount % 2 == 0 ? WHITE : BLACK;
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 (Row 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, r, f)) == next_player
158 && piece_moves_allowed(gamestate, r, f, moves) > 0) { 158 && piece_moves_allowed(gamestate, r, f, moves) > 0) {
159 return false; 159 return false;
160 } 160 }
201 /* count the available pieces */ 201 /* count the available pieces */
202 unsigned piece_count[7] = {0}; 202 unsigned piece_count[7] = {0};
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 (Row 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, r, f);
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) {
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, mdst(move)) == 0) { 323 piece_at(gamestate, move->torank, move->tofile) == 0) {
324 piece_remove(gamestate, move->fromrow, move->tofile); 324 piece_remove(gamestate, move->fromrank, move->tofile);
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, 3, file);
330 enpassant_threat_remove(gamestate, 4, file); 330 enpassant_threat_remove(gamestate, 4, file);
331 } 331 }
332 332
333 /* move (and maybe capture or promote) */ 333 /* move (and maybe capture or promote) */
334 piece_remove(gamestate, msrc(move)); 334 piece_remove(gamestate, move->fromrank, move->fromfile);
335 if (move->promotion) { 335 if (move->promotion) {
336 piece_set(gamestate, mdst(move), move->promotion); 336 piece_set(gamestate, move->torank, move->tofile, move->promotion);
337 } else { 337 } else {
338 piece_set(gamestate, mdst(move), move->piece); 338 piece_set(gamestate, move->torank, move->tofile, 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->fromrow == 1 && move->torow == 3) || 343 (move->fromrank == 1 && move->torank == 3) ||
344 (move->fromrow == 6 && move->torow == 4))) { 344 (move->fromrank == 6 && move->torank == 4))) {
345 enpassant_threat_add(gamestate, move->torow, move->tofile); 345 enpassant_threat_add(gamestate, move->torank, move->tofile);
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);
351 if (move->tofile == fileidx('g')) { 351 if (move->tofile == fileidx('g')) {
352 gamestate->board[move->torow][fileidx('h')] = 0; 352 gamestate->board[move->torank][fileidx('h')] = 0;
353 gamestate->board[move->torow][fileidx('f')] = mkpiece(ROOK, color); 353 gamestate->board[move->torank][fileidx('f')] = mkpiece(ROOK, color);
354 } else if (move->tofile == fileidx('c')) { 354 } else if (move->tofile == fileidx('c')) {
355 gamestate->board[move->torow][fileidx('a')] = 0; 355 gamestate->board[move->torank][fileidx('a')] = 0;
356 gamestate->board[move->torow][fileidx('d')] = mkpiece(ROOK, color); 356 gamestate->board[move->torank][fileidx('d')] = mkpiece(ROOK, color);
357 } 357 }
358 } 358 }
359 359
360 /* add move to the moves array and the new position to the FEN array */ 360 /* add move to the moves array and the new position to the FEN array */
361 if (gamestate->movecount == gamestate->movecapacity) { 361 if (gamestate->movecount == gamestate->movecapacity) {
419 419
420 /* find the opposing king */ 420 /* find the opposing king */
421 Color piececolor = piece_color(move->piece); 421 Color piececolor = 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 Row opkingrow = 0; 424 Rank opkingrank = 0;
425 for (Row row = 0 ; row < 8 ; row++) { 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, row, file); 427 Piece p = piece_at(&simulation, rank, file);
428 if (p == mkpiece(KING, oppcolor)) { 428 if (p == mkpiece(KING, oppcolor)) {
429 opkingfile = file; 429 opkingfile = file;
430 opkingrow = row; 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, opkingrow, opkingfile, 438 bool incheck = get_threats(&simulation, opkingrank, opkingfile,
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;
446 /* determine possible escape fields */ 446 /* determine possible escape fields */
447 bool canescape = false; 447 bool canescape = false;
448 for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { 448 for (int dr = -1 ; dr <= 1 && !canescape ; dr++) {
449 for (int df = -1 ; df <= 1 && !canescape ; df++) { 449 for (int df = -1 ; df <= 1 && !canescape ; df++) {
450 if (dr == 0 && df == 0) continue; 450 if (dr == 0 && df == 0) continue;
451 Row er = opkingrow + dr; 451 Rank er = opkingrank + dr;
452 File ef = opkingfile + df; 452 File ef = opkingfile + df;
453 if (!isidx(er) || !isidx(ef)) continue; 453 if (!isidx(er) || !isidx(ef)) continue;
454 454
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)
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 */
466 GameState sim_retaliate = gamestate_copy_sim(&simulation); 466 GameState sim_retaliate = gamestate_copy_sim(&simulation);
467 Move move_retaliate = {0}; 467 Move move_retaliate = {0};
468 move_retaliate.piece = mkpiece(KING, oppcolor); 468 move_retaliate.piece = mkpiece(KING, oppcolor);
469 move_retaliate.fromrow = opkingrow; 469 move_retaliate.fromrank = opkingrank;
470 move_retaliate.fromfile = opkingfile; 470 move_retaliate.fromfile = opkingfile;
471 move_retaliate.torow = 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, er, ef, piececolor);
476 gamestate_cleanup(&sim_retaliate); 476 gamestate_cleanup(&sim_retaliate);
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].fromrow, threats[0].fromfile, oppcolor); 488 threats[0].fromrank, threats[0].fromfile, 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]);
494 unsigned tptype = piece_type(threat->piece); 494 unsigned tptype = piece_type(threat->piece);
495 495
496 /* knight, pawns and the king cannot be blocked */ 496 /* knight, pawns and the king cannot be blocked */
497 if (tptype == BISHOP || tptype == ROOK || tptype == QUEEN) { 497 if (tptype == BISHOP || tptype == ROOK || tptype == QUEEN) {
498 if (threat->fromrow == threat->torow) { 498 if (threat->fromrank == threat->torank) {
499 /* rook aspect (on row) */ 499 /* rook aspect (on rank) */
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->torow, file, oppcolor); 505 threat->torank, file, 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->torow > threat->fromrow ? 1 : -1; 509 int d = threat->torank > threat->fromrank ? 1 : -1;
510 Row row = threat->fromrow; 510 Rank rank = threat->fromrank;
511 while (!canescape && row != threat->torow - d) { 511 while (!canescape && rank != threat->torank - d) {
512 row += d; 512 rank += d;
513 canescape |= is_protected(&simulation, 513 canescape |= is_protected(&simulation,
514 row, threat->tofile, oppcolor); 514 rank, threat->tofile, oppcolor);
515 } 515 }
516 } else { 516 } else {
517 /* bishop aspect */ 517 /* bishop aspect */
518 int dr = threat->torow > threat->fromrow ? 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;
520 520
521 Row row = threat->fromrow; 521 Rank rank = threat->fromrank;
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 && row != threat->torow - dr) { 524 && rank != threat->torank - dr) {
525 row += dr; 525 rank += dr;
526 file += df; 526 file += df;
527 canescape |= is_protected(&simulation, row, file, 527 canescape |= is_protected(&simulation, rank, file,
528 oppcolor); 528 oppcolor);
529 } 529 }
530 } 530 }
531 } 531 }
532 } 532 }
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->torow, move->tofile, 569 if (get_real_candidates(gamestate, move->torank, move->tofile,
570 piece_color(move->piece), candidates, &ccount)) { 570 piece_color(move->piece), candidates, &ccount)) {
571 unsigned int ambrows = 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++;
575 if (candidates[i].fromrow == move->fromrow) { 575 if (candidates[i].fromrank == move->fromrank) {
576 ambrows++; 576 ambranks++;
577 } 577 }
578 if (candidates[i].fromfile == move->fromfile) { 578 if (candidates[i].fromfile == move->fromfile) {
579 ambfiles++; 579 ambfiles++;
580 } 580 }
581 } 581 }
582 } 582 }
583 /* neither file, nor row are ambiguous, name file */ 583 /* neither file, nor rank are ambiguous, name file */
584 if (ambpiece > 1 && ambrows == 1 && ambfiles == 1) { 584 if (ambpiece > 1 && ambranks == 1 && ambfiles == 1) {
585 /* this is most likely the case with Knights 585 /* this is most likely the case with Knights
586 * in diagonal opposition */ 586 * in diagonal opposition */
587 string[idx++] = filechr(move->fromfile); 587 string[idx++] = filechr(move->fromfile);
588 } else { 588 } else {
589 /* ambiguous row, name file */ 589 /* ambiguous rank, name file */
590 if (ambrows > 1) { 590 if (ambranks > 1) {
591 string[idx++] = filechr(move->fromfile); 591 string[idx++] = filechr(move->fromfile);
592 } 592 }
593 /* ambiguous file, name row */ 593 /* ambiguous file, name rank */
594 if (ambfiles > 1) { 594 if (ambfiles > 1) {
595 string[idx++] = rowchr(move->fromrow); 595 string[idx++] = rankchr(move->fromrank);
596 } 596 }
597 } 597 }
598 } 598 }
599 } 599 }
600 600
603 string[idx++] = 'x'; 603 string[idx++] = 'x';
604 } 604 }
605 605
606 /* destination */ 606 /* destination */
607 string[idx++] = filechr(move->tofile); 607 string[idx++] = filechr(move->tofile);
608 string[idx++] = rowchr(move->torow); 608 string[idx++] = rankchr(move->torank);
609 609
610 /* promotion? */ 610 /* promotion? */
611 if (move->promotion) { 611 if (move->promotion) {
612 string[idx++] = '='; 612 string[idx++] = '=';
613 string[idx++] = getpiecechr(move->promotion); 613 string[idx++] = getpiecechr(move->promotion);
624 624
625 static int validate_move_rules(const GameState *gamestate, const Move *move) { 625 static int validate_move_rules(const GameState *gamestate, const Move *move) {
626 assert((move->piece & ~(PIECE_MASK|COLOR_MASK)) == 0); 626 assert((move->piece & ~(PIECE_MASK|COLOR_MASK)) == 0);
627 627
628 /* validate indices (don't trust opponent) */ 628 /* validate indices (don't trust opponent) */
629 if (!isidx(move->fromrow) || !isidx(move->fromfile) || 629 if (!isidx(move->fromrank) || !isidx(move->fromfile) ||
630 !isidx(move->torow) || !isidx(move->tofile)) { 630 !isidx(move->torank) || !isidx(move->tofile)) {
631 return INVALID_MOVE_SYNTAX; 631 return INVALID_MOVE_SYNTAX;
632 } 632 }
633 633
634 /* must move */ 634 /* must move */
635 if (move->fromfile == move->tofile && move->fromrow == move->torow) { 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, msrc(move)) != move->piece) { 640 if (piece_at(gamestate, move->fromrank, move->fromfile) != 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, mdst(move)); 645 Piece piece_at_dst = piece_at(gamestate, move->torank, move->tofile);
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->fromrow, move->tofile)) { 657 move->fromrank, move->tofile)) {
658 return RULES_VIOLATED; 658 return RULES_VIOLATED;
659 } 659 }
660 } 660 }
661 661
662 /* validate individual rules */ 662 /* validate individual rules */
704 Move simmove = *move; 704 Move simmove = *move;
705 apply_move_internal(&simulation, &simmove); 705 apply_move_internal(&simulation, &simmove);
706 Color piececolor = piece_color(move->piece); 706 Color piececolor = piece_color(move->piece);
707 Color oppcolor = opponent_color(piececolor); 707 Color oppcolor = opponent_color(piececolor);
708 File kingfile = 0; 708 File kingfile = 0;
709 Row kingrow = 0; 709 Rank kingrank = 0;
710 for (Row row = 0 ; row < 8 ; row++) { 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, row, file); 712 Piece p = piece_at(&simulation, rank, file);
713 if (p == mkpiece(KING, piececolor)) { 713 if (p == mkpiece(KING, piececolor)) {
714 kingfile = file; 714 kingfile = file;
715 kingrow = row; 715 kingrank = rank;
716 } 716 }
717 } 717 }
718 } 718 }
719 if (is_covered(&simulation, kingrow, kingfile, oppcolor)) { 719 if (is_covered(&simulation, kingrank, kingfile, 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, Row row, File file) { 755 Piece piece_at(const GameState *gamestate, Rank rank, File file) {
756 return gamestate->board[row][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, Row row, File file, Piece piece) { 759 void piece_set(GameState *gamestate, Rank rank, File file, Piece piece) {
760 gamestate->board[row][file] = piece; 760 gamestate->board[rank][file] = piece;
761 } 761 }
762 762
763 bool get_candidates(const GameState *gamestate, Row row, File file, 763 bool get_candidates(const GameState *gamestate, Rank rank, File file,
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 (Row 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, r, f);
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].fromrow = r; 774 candidates[ccount].fromrank = r;
775 candidates[ccount].fromfile = f; 775 candidates[ccount].fromfile = f;
776 candidates[ccount].torow = row; 776 candidates[ccount].torank = rank;
777 candidates[ccount].tofile = file; 777 candidates[ccount].tofile = file;
778 if (piece_type(p) == PAWN && (row == 0 || row == 7)) { 778 if (piece_type(p) == PAWN && (rank == 0 || rank == 7)) {
779 /* the exact piece for promotion does not matter */ 779 /* the exact piece for promotion does not matter */
780 candidates[ccount].promotion = mkpiece(QUEEN, color); 780 candidates[ccount].promotion = mkpiece(QUEEN, color);
781 } 781 }
782 ccount++; 782 ccount++;
783 783
807 } 807 }
808 808
809 return result; 809 return result;
810 } 810 }
811 811
812 bool get_threats(const GameState *gamestate, Row row, File file, 812 bool get_threats(const GameState *gamestate, Rank rank, File file,
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, row, file)) != opcolor) { 818 if (piece_color(piece_at(&simulation, rank, file)) != 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, row, file, mkpiece(PAWN, opcolor)); 820 piece_set(&simulation, rank, file, 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 (Row 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, r, f);
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].fromrow = r; 831 candidates[ccount].fromrank = r;
832 candidates[ccount].fromfile = f; 832 candidates[ccount].fromfile = f;
833 candidates[ccount].torow = row; 833 candidates[ccount].torank = rank;
834 candidates[ccount].tofile = file; 834 candidates[ccount].tofile = file;
835 candidates[ccount].capture = true; 835 candidates[ccount].capture = true;
836 if (piece_type(p) == PAWN && (row == 0 || row == 7)) { 836 if (piece_type(p) == PAWN && (rank == 0 || rank == 7)) {
837 /* the exact piece for promotion does not matter */ 837 /* the exact piece for promotion does not matter */
838 candidates[ccount].promotion = mkpiece(QUEEN, color); 838 candidates[ccount].promotion = mkpiece(QUEEN, color);
839 } 839 }
840 ccount++; 840 ccount++;
841 } 841 }
869 GameState simulation = gamestate_copy_sim(gamestate); 869 GameState simulation = gamestate_copy_sim(gamestate);
870 Move simmove = *move; 870 Move simmove = *move;
871 apply_move_internal(&simulation, &simmove); 871 apply_move_internal(&simulation, &simmove);
872 872
873 File kingfile = 0; 873 File kingfile = 0;
874 Row kingrow = 0; 874 Rank kingrank = 0;
875 for (Row row = 0 ; row < 8 ; row++) { 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, row, file) == mkpiece(KING, color)) { 877 if (piece_at(&simulation, rank, file) == mkpiece(KING, color)) {
878 kingfile = file; 878 kingfile = file;
879 kingrow = row; 879 kingrank = rank;
880 } 880 }
881 } 881 }
882 } 882 }
883 883
884 bool covered = is_covered(&simulation, 884 bool covered = is_covered(&simulation,
885 kingrow, kingfile, opponent_color(color)); 885 kingrank, kingfile, 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, Row row, File file, 891 bool get_real_candidates(const GameState *gamestate, Rank rank, File file,
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, row, file, color, candidates, &ccount)) { 900 if (get_candidates(gamestate, rank, file, 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, Row row, File file, 916 bool get_real_threats(const GameState *gamestate, Rank rank, File file,
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, row, file, color, candidates, &ccount)) { 925 if (get_threats(gamestate, rank, file, 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->torow, move->tofile, color, 953 if (get_candidates(gamestate, move->torank, move->tofile, 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++) {
959 /* filter by partial fromrow/fromfile information */ 959 /* filter by partial fromrank/fromfile information */
960 if (candidates[i].piece == move->piece && 960 if (candidates[i].piece == move->piece &&
961 (move->fromrow == POS_UNSPECIFIED || 961 (move->fromrank == POS_UNSPECIFIED ||
962 move->fromrow == candidates[i].fromrow) && 962 move->fromrank == candidates[i].fromrank) &&
963 (move->fromfile == POS_UNSPECIFIED || 963 (move->fromfile == POS_UNSPECIFIED ||
964 move->fromfile == candidates[i].fromfile)) { 964 move->fromfile == candidates[i].fromfile)) {
965 965
966 /* found a candidate, here it does not matter if it's valid! */ 966 /* found a candidate, here it does not matter if it's valid! */
967 found = true; 967 found = true;
992 return PIECE_NOT_FOUND; 992 return PIECE_NOT_FOUND;
993 } 993 }
994 } 994 }
995 995
996 /* found a candidate, copy the source location */ 996 /* found a candidate, copy the source location */
997 move->fromrow = candidate->fromrow; 997 move->fromrank = candidate->fromrank;
998 move->fromfile = candidate->fromfile; 998 move->fromfile = candidate->fromfile;
999 return VALID_MOVE_SYNTAX; 999 return VALID_MOVE_SYNTAX;
1000 } else { 1000 } else {
1001 return PIECE_NOT_FOUND; 1001 return PIECE_NOT_FOUND;
1002 } 1002 }
1003 } 1003 }
1004 1004
1005 static int eval_move1(const char *pstr, Move *move, Color color) { 1005 static int eval_move1(const char *pstr, Move *move, Color color) {
1006 memset(move, 0, sizeof(Move)); 1006 memset(move, 0, sizeof(Move));
1007 move->fromfile = POS_UNSPECIFIED; 1007 move->fromfile = POS_UNSPECIFIED;
1008 move->fromrow = POS_UNSPECIFIED; 1008 move->fromrank = POS_UNSPECIFIED;
1009 1009
1010 size_t len = strlen(pstr); 1010 size_t len = strlen(pstr);
1011 if (len < 1 || len > 6) { 1011 if (len < 1 || len > 6) {
1012 return INVALID_MOVE_SYNTAX; 1012 return INVALID_MOVE_SYNTAX;
1013 } 1013 }
1035 1035
1036 if (len == 2) { 1036 if (len == 2) {
1037 /* pawn move (e.g. "e4") */ 1037 /* pawn move (e.g. "e4") */
1038 move->piece = mkpiece(PAWN, color); 1038 move->piece = mkpiece(PAWN, color);
1039 move->tofile = fileidx(mstr[0]); 1039 move->tofile = fileidx(mstr[0]);
1040 move->torow = rowidx(mstr[1]); 1040 move->torank = rankidx(mstr[1]);
1041 } else if (len == 3) { 1041 } else if (len == 3) {
1042 if (strcmp(mstr, "O-O") == 0) { 1042 if (strcmp(mstr, "O-O") == 0) {
1043 /* king side castling */ 1043 /* king side castling */
1044 move->piece = mkpiece(KING, color); 1044 move->piece = mkpiece(KING, color);
1045 move->fromfile = fileidx('e'); 1045 move->fromfile = fileidx('e');
1046 move->tofile = fileidx('g'); 1046 move->tofile = fileidx('g');
1047 move->fromrow = move->torow = color == WHITE ? 0 : 7; 1047 move->fromrank = move->torank = color == WHITE ? 0 : 7;
1048 } else { 1048 } else {
1049 /* move (e.g. "Nf3") */ 1049 /* move (e.g. "Nf3") */
1050 move->piece = getpiece(mstr[0], color); 1050 move->piece = getpiece(mstr[0], color);
1051 move->tofile = fileidx(mstr[1]); 1051 move->tofile = fileidx(mstr[1]);
1052 move->torow = rowidx(mstr[2]); 1052 move->torank = rankidx(mstr[2]);
1053 } 1053 }
1054 } else if (len == 4) { 1054 } else if (len == 4) {
1055 move->piece = getpiece(mstr[0], color); 1055 move->piece = getpiece(mstr[0], color);
1056 if (move->piece == 0) { 1056 if (move->piece == 0) {
1057 move->piece = mkpiece(PAWN, color); 1057 move->piece = mkpiece(PAWN, color);
1068 if (piece_type(move->piece) == PAWN) { 1068 if (piece_type(move->piece) == PAWN) {
1069 /* invalidate the result */ 1069 /* invalidate the result */
1070 move->piece = 0; 1070 move->piece = 0;
1071 } 1071 }
1072 } else { 1072 } else {
1073 move->fromrow = rowidx(mstr[1]); 1073 move->fromrank = rankidx(mstr[1]);
1074 } 1074 }
1075 } 1075 }
1076 move->tofile = fileidx(mstr[2]); 1076 move->tofile = fileidx(mstr[2]);
1077 move->torow = rowidx(mstr[3]); 1077 move->torank = rankidx(mstr[3]);
1078 } else if (len == 5) { 1078 } else if (len == 5) {
1079 if (strcmp(mstr, "O-O-O") == 0) { 1079 if (strcmp(mstr, "O-O-O") == 0) {
1080 /* queen side castling "O-O-O" */ 1080 /* queen side castling "O-O-O" */
1081 move->piece = mkpiece(KING, color); 1081 move->piece = mkpiece(KING, color);
1082 move->fromfile = fileidx('e'); 1082 move->fromfile = fileidx('e');
1083 move->tofile = fileidx('c'); 1083 move->tofile = fileidx('c');
1084 move->fromrow = move->torow = color == WHITE ? 0 : 7; 1084 move->fromrank = move->torank = color == WHITE ? 0 : 7;
1085 } else { 1085 } else {
1086 move->piece = getpiece(mstr[0], color); 1086 move->piece = getpiece(mstr[0], color);
1087 if (mstr[2] == 'x') { 1087 if (mstr[2] == 'x') {
1088 move->capture = true; 1088 move->capture = true;
1089 if (move->piece) { 1089 if (move->piece) {
1090 /* capture (e.g. "Ndxf3" or "R1xh3") */ 1090 /* capture (e.g. "Ndxf3" or "R1xh3") */
1091 if (isfile(mstr[1])) { 1091 if (isfile(mstr[1])) {
1092 move->fromfile = fileidx(mstr[1]); 1092 move->fromfile = fileidx(mstr[1]);
1093 } else if (isrow(mstr[1])) { 1093 } else if (isrank(mstr[1])) {
1094 move->fromrow = rowidx(mstr[1]); 1094 move->fromrank = rankidx(mstr[1]);
1095 } else { 1095 } else {
1096 return INVALID_MOVE_SYNTAX; 1096 return INVALID_MOVE_SYNTAX;
1097 } 1097 }
1098 } else { 1098 } else {
1099 /* long notation capture (e.g. "e5xf6") */ 1099 /* long notation capture (e.g. "e5xf6") */
1100 move->piece = mkpiece(PAWN, color); 1100 move->piece = mkpiece(PAWN, color);
1101 move->fromfile = fileidx(mstr[0]); 1101 move->fromfile = fileidx(mstr[0]);
1102 move->fromrow = rowidx(mstr[1]); 1102 move->fromrank = rankidx(mstr[1]);
1103 } 1103 }
1104 } else { 1104 } else {
1105 /* long notation move (e.g. "Nc5a4") */ 1105 /* long notation move (e.g. "Nc5a4") */
1106 move->fromfile = fileidx(mstr[1]); 1106 move->fromfile = fileidx(mstr[1]);
1107 move->fromrow = rowidx(mstr[2]); 1107 move->fromrank = rankidx(mstr[2]);
1108 } 1108 }
1109 move->tofile = fileidx(mstr[3]); 1109 move->tofile = fileidx(mstr[3]);
1110 move->torow = rowidx(mstr[4]); 1110 move->torank = rankidx(mstr[4]);
1111 } 1111 }
1112 } else if (len == 6) { 1112 } else if (len == 6) {
1113 /* long notation capture (e.g. "Nc5xf3") */ 1113 /* long notation capture (e.g. "Nc5xf3") */
1114 if (mstr[3] == 'x') { 1114 if (mstr[3] == 'x') {
1115 move->capture = true; 1115 move->capture = true;
1116 move->piece = getpiece(mstr[0], color); 1116 move->piece = getpiece(mstr[0], color);
1117 move->fromfile = fileidx(mstr[1]); 1117 move->fromfile = fileidx(mstr[1]);
1118 move->fromrow = rowidx(mstr[2]); 1118 move->fromrank = rankidx(mstr[2]);
1119 move->tofile = fileidx(mstr[4]); 1119 move->tofile = fileidx(mstr[4]);
1120 move->torow = rowidx(mstr[5]); 1120 move->torank = rankidx(mstr[5]);
1121 } 1121 }
1122 } 1122 }
1123 1123
1124 1124
1125 if (!move->piece) { 1125 if (!move->piece) {
1126 return INVALID_MOVE_SYNTAX; 1126 return INVALID_MOVE_SYNTAX;
1127 } 1127 }
1128 1128
1129 if (piece_type(move->piece) == PAWN 1129 if (piece_type(move->piece) == PAWN
1130 && move->torow == (color==WHITE?7:0) 1130 && move->torank == (color==WHITE?7:0)
1131 && !move->promotion) { 1131 && !move->promotion) {
1132 return NEED_PROMOTION; 1132 return NEED_PROMOTION;
1133 } 1133 }
1134 1134
1135 /* up to this point 1135 /* up to this point
1136 * destination indices must be specified and valid 1136 * destination indices must be specified and valid
1137 * source indices must either be valid or unspecified 1137 * source indices must either be valid or unspecified
1138 */ 1138 */
1139 if (!isidxr(move->fromrow) || !isidxr(move->fromfile) || 1139 if (!isidxr(move->fromrank) || !isidxr(move->fromfile) ||
1140 !isidx(move->torow) || !isidx(move->tofile)) { 1140 !isidx(move->torank) || !isidx(move->tofile)) {
1141 return INVALID_MOVE_SYNTAX; 1141 return INVALID_MOVE_SYNTAX;
1142 } 1142 }
1143 1143
1144 return VALID_MOVE_SYNTAX; 1144 return VALID_MOVE_SYNTAX;
1145 } 1145 }
1147 static int eval_move2(const GameState *gamestate, 1147 static int eval_move2(const GameState *gamestate,
1148 const char *mstr, Color color, Move *move, bool lazy) { 1148 const char *mstr, Color color, Move *move, bool lazy) {
1149 int result = eval_move1(mstr, move, color); 1149 int result = eval_move1(mstr, move, color);
1150 if (result == VALID_MOVE_SYNTAX) { 1150 if (result == VALID_MOVE_SYNTAX) {
1151 if (move->fromfile == POS_UNSPECIFIED 1151 if (move->fromfile == POS_UNSPECIFIED
1152 || move->fromrow == POS_UNSPECIFIED) { 1152 || move->fromrank == POS_UNSPECIFIED) {
1153 result = getlocation(gamestate, move); 1153 result = getlocation(gamestate, move);
1154 } 1154 }
1155 if (result == VALID_MOVE_SYNTAX) { 1155 if (result == VALID_MOVE_SYNTAX) {
1156 /* correct check/checkmate flags */ 1156 /* correct check/checkmate flags */
1157 if (lazy) { 1157 if (lazy) {
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, Row row, File file, Color color) { 1189 bool is_protected(const GameState *gamestate, Rank rank, File file, Color color) {
1190 Move candidates[16]; 1190 Move candidates[16];
1191 size_t ccount; 1191 size_t ccount;
1192 /* we need all candidates - not only threats! */ 1192 /* we need all candidates - not only threats! */
1193 if (get_candidates(gamestate, row, file, color, candidates, &ccount)) { 1193 if (get_candidates(gamestate, rank, file, color, candidates, &ccount)) {
1194 for (size_t i = 0 ; i < ccount ; i++) { 1194 for (size_t i = 0 ; i < ccount ; i++) {
1195 /* skip the king */ 1195 /* skip the king */
1196 if (piece_type(candidates[i].piece) == KING) continue; 1196 if (piece_type(candidates[i].piece) == KING) continue;
1197 /* skip pinned pieces */ 1197 /* skip pinned pieces */
1198 if (is_pinned(gamestate, &candidates[i])) continue; 1198 if (is_pinned(gamestate, &candidates[i])) continue;
1271 return snprintf(str, 6, "%02u:%02u", minutes, seconds); 1271 return snprintf(str, 6, "%02u:%02u", minutes, seconds);
1272 } 1272 }
1273 } 1273 }
1274 1274
1275 size_t filter_moves_allowed(const GameState *gamestate, 1275 size_t filter_moves_allowed(const GameState *gamestate,
1276 Color c, Row r, File f, Move *moves, moves_generator_func func) { 1276 Color c, Rank r, File f, Move *moves, moves_generator_func func) {
1277 1277
1278 /* worst case: the queen has the most moves */ 1278 /* worst case: the queen has the most moves */
1279 Move candidates[QUEEN_MOVES_MAX]; 1279 Move candidates[QUEEN_MOVES_MAX];
1280 size_t candidatecount = func(gamestate, c, r, f, candidates); 1280 size_t candidatecount = func(gamestate, c, r, f, candidates);
1281 size_t count = 0; 1281 size_t count = 0;

mercurial