src/chess/rules.c

changeset 49
02c509a44e98
parent 48
0cedda2544da
child 50
41017d0a72c5
equal deleted inserted replaced
48:0cedda2544da 49:02c509a44e98
52 elem = elem->next; 52 elem = elem->next;
53 free(cur); 53 free(cur);
54 }; 54 };
55 } 55 }
56 56
57 /* MUST be called IMMEDIATLY after applying a move to work correctly */
58 static void format_move(GameState *gamestate, Move *move) {
59 char *string = move->string;
60
61 /* at least 8 characters should be available, wipe them out */
62 memset(string, 0, 8);
63
64 /* special formats for castling */
65 if ((move->piece&PIECE_MASK) == KING &&
66 abs(move->tofile-move->fromfile) == 2) {
67 if (move->tofile==fileidx('c')) {
68 memcpy(string, "O-O-O", 5);
69 } else {
70 memcpy(string, "O-O", 3);
71 }
72 }
73
74 /* start by notating the piece character */
75 string[0] = getpiecechr(move->piece);
76 int idx = string[0] ? 1 : 0;
77
78 /* find out how many source information we do need */
79 uint8_t piece = move->piece & PIECE_MASK;
80 if (piece == PAWN) {
81 if (move->capture) {
82 string[idx++] = filechr(move->fromfile);
83 }
84 } else if (piece != KING) {
85 Move threats[16];
86 uint8_t threatcount;
87 get_real_threats(gamestate, move->torow, move->tofile,
88 move->piece&COLOR_MASK, threats, &threatcount);
89 if (threatcount > 1) {
90 int ambrows = 0, ambfiles = 0;
91 for (uint8_t i = 0 ; i < threatcount ; i++) {
92 if (threats[i].fromrow == move->fromrow) {
93 ambrows++;
94 }
95 if (threats[i].fromfile == move->fromfile) {
96 ambfiles++;
97 }
98 }
99 /* ambiguous row, name file */
100 if (ambrows > 1) {
101 string[idx++] = filechr(move->fromfile);
102 }
103 /* ambiguous file, name row */
104 if (ambfiles > 1) {
105 string[idx++] = filechr(move->fromrow);
106 }
107 }
108 }
109
110 /* capturing? */
111 if (move->capture) {
112 string[idx++] = 'x';
113 }
114
115 /* destination */
116 string[idx++] = filechr(move->tofile);
117 string[idx++] = rowchr(move->torow);
118
119 /* promotion? */
120 if (move->promotion) {
121 string[idx++] = '=';
122 string[idx++] = getpiecechr(move->promotion);
123 }
124
125 /* check? */
126 if (move->check) {
127 /* works only, if this function is called when applying the move */
128 string[idx++] = gamestate->checkmate?'#':'+';
129 }
130 }
131
57 static void addmove(GameState* gamestate, Move *move) { 132 static void addmove(GameState* gamestate, Move *move) {
58 MoveList *elem = malloc(sizeof(MoveList)); 133 MoveList *elem = malloc(sizeof(MoveList));
59 elem->next = NULL; 134 elem->next = NULL;
60 elem->move = *move; 135 elem->move = *move;
61 136
107 case 'K': return KING; 182 case 'K': return KING;
108 default: return 0; 183 default: return 0;
109 } 184 }
110 } 185 }
111 186
112 void apply_move(GameState *gamestate, Move *move) { 187 static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) {
113 uint8_t piece = move->piece & PIECE_MASK; 188 uint8_t piece = move->piece & PIECE_MASK;
114 uint8_t color = move->piece & COLOR_MASK; 189 uint8_t color = move->piece & COLOR_MASK;
115 190
116 /* en passant capture */ 191 /* en passant capture */
117 if (move->capture && piece == PAWN && 192 if (move->capture && piece == PAWN &&
139 } else { 214 } else {
140 mdst(gamestate->board, move) = move->piece; 215 mdst(gamestate->board, move) = move->piece;
141 } 216 }
142 217
143 /* castling */ 218 /* castling */
144 if (piece == KING && 219 if (piece == KING && move->fromfile == fileidx('e')) {
145 move->fromfile == fileidx('e')) {
146 220
147 if (move->tofile == fileidx('g')) { 221 if (move->tofile == fileidx('g')) {
148 gamestate->board[move->torow][fileidx('h')] = 0; 222 gamestate->board[move->torow][fileidx('h')] = 0;
149 gamestate->board[move->torow][fileidx('f')] = color|ROOK; 223 gamestate->board[move->torow][fileidx('f')] = color|ROOK;
150 } else if (move->tofile == fileidx('c')) { 224 } else if (move->tofile == fileidx('c')) {
151 gamestate->board[move->torow][fileidx('a')] = 0; 225 gamestate->board[move->torow][fileidx('a')] = 0;
152 gamestate->board[move->torow][fileidx('d')] = color|ROOK; 226 gamestate->board[move->torow][fileidx('d')] = color|ROOK;
153 } 227 }
154 } 228 }
155 229
230 if (!simulate) {
231 if (!move->string[0]) {
232 format_move(gamestate, move);
233 }
234 }
235 /* add move, even in simulation (checkmate test needs it) */
156 addmove(gamestate, move); 236 addmove(gamestate, move);
237 }
238
239 void apply_move(GameState *gamestate, Move *move) {
240 apply_move_impl(gamestate, move, 0);
157 } 241 }
158 242
159 static int validate_move_rules(GameState *gamestate, Move *move) { 243 static int validate_move_rules(GameState *gamestate, Move *move) {
160 /* validate indices (don't trust opponent) */ 244 /* validate indices (don't trust opponent) */
161 if (!chkidx(move)) { 245 if (!chkidx(move)) {
246 } 330 }
247 331
248 /* simulate move for check validation */ 332 /* simulate move for check validation */
249 GameState simulation = gamestate_copy_sim(gamestate); 333 GameState simulation = gamestate_copy_sim(gamestate);
250 Move simmove = *move; 334 Move simmove = *move;
251 apply_move(&simulation, &simmove); 335 apply_move_impl(&simulation, &simmove, 1);
252 336
253 /* don't move into or stay in check position */ 337 /* don't move into or stay in check position */
254 if (is_covered(&simulation, mykingrow, mykingfile, 338 if (is_covered(&simulation, mykingrow, mykingfile,
255 opponent_color(piececolor))) { 339 opponent_color(piececolor))) {
256 340
446 result = 1; 530 result = 1;
447 if (threats && threatcount) { 531 if (threats && threatcount) {
448 threats[(*threatcount)++] = candidates[i]; 532 threats[(*threatcount)++] = candidates[i];
449 } 533 }
450 } 534 }
451 gamestate_cleanup(&simulation);
452 } 535 }
453 536
454 return result; 537 return result;
455 } else { 538 } else {
456 return 0; 539 return 0;
465 Move threats[16], *threat = NULL; 548 Move threats[16], *threat = NULL;
466 uint8_t threatcount; 549 uint8_t threatcount;
467 550
468 if (get_threats(gamestate, move->torow, move->tofile, color, 551 if (get_threats(gamestate, move->torow, move->tofile, color,
469 threats, &threatcount)) { 552 threats, &threatcount)) {
553
554 int reason = INVALID_POSITION;
470 555
471 // find threats for the specified position 556 // find threats for the specified position
472 for (uint8_t i = 0 ; i < threatcount ; i++) { 557 for (uint8_t i = 0 ; i < threatcount ; i++) {
473 if ((threats[i].piece & (PIECE_MASK | COLOR_MASK)) 558 if ((threats[i].piece & (PIECE_MASK | COLOR_MASK))
474 == move->piece && 559 == move->piece &&
478 move->fromfile == threats[i].fromfile)) { 563 move->fromfile == threats[i].fromfile)) {
479 564
480 if (threat) { 565 if (threat) {
481 return AMBIGUOUS_MOVE; 566 return AMBIGUOUS_MOVE;
482 } else { 567 } else {
483 threat = &(threats[i]); 568 // found threat is no real threat
569 if (is_pinned(gamestate, &(threats[i]))) {
570 reason = incheck?KING_IN_CHECK:PIECE_PINNED;
571 } else {
572 threat = &(threats[i]);
573 }
484 } 574 }
485 } 575 }
486 } 576 }
487 577
488 // can't threaten specified position 578 // can't threaten specified position
489 if (!threat) { 579 if (!threat) {
490 return INVALID_POSITION; 580 return reason;
491 } 581 }
492 582
493 // found threat is no real threat 583 memcpy(move, threat, sizeof(Move));
494 if (is_pinned(gamestate, threat)) { 584 return VALID_MOVE_SYNTAX;
495 return incheck?KING_IN_CHECK:PIECE_PINNED;
496 } else {
497 memcpy(move, threat, sizeof(Move));
498 return VALID_MOVE_SYNTAX;
499 }
500 } else { 585 } else {
501 return INVALID_POSITION; 586 return INVALID_POSITION;
502 } 587 }
503 } 588 }
504 589

mercurial