src/chess/rules.c

changeset 29
c6a1ad6cf749
parent 28
0c1371488d87
child 33
866025982aa9
equal deleted inserted replaced
28:0c1371488d87 29:c6a1ad6cf749
88 case KING: return king_getlocation(gamestate, move); 88 case KING: return king_getlocation(gamestate, move);
89 default: return INVALID_MOVE_SYNTAX; 89 default: return INVALID_MOVE_SYNTAX;
90 } 90 }
91 } 91 }
92 92
93 _Bool get_any_threat_for(GameState *gamestate, uint8_t row, uint8_t file,
94 uint8_t color, Move *threat) {
95 Move threats[16];
96 int threatcount = 0;
97 for (uint8_t r = 0 ; r < 8 ; r++) {
98 for (uint8_t f = 0 ; f < 8 ; f++) {
99 if ((gamestate->board[r][f] & COLOR_MASK) == color) {
100 memset(&(threats[threatcount]), 0, sizeof(Move));
101 threats[threatcount].piece = gamestate->board[r][f];
102 threats[threatcount].fromrow = r;
103 threats[threatcount].fromfile = f;
104 threats[threatcount].torow = row;
105 threats[threatcount].tofile = file;
106 threatcount++;
107 }
108 }
109 }
110
111 for (int i = 0 ; i < threatcount ; i++) {
112 if (validate_move(gamestate, &(threats[i]))) {
113 if (threat) {
114 *threat = threats[i];
115 }
116 return 1;
117 }
118 }
119
120 return 0;
121 }
122
123 void apply_move(GameState *gamestate, Move *move) { 93 void apply_move(GameState *gamestate, Move *move) {
124 uint8_t piece = move->piece & PIECE_MASK; 94 uint8_t piece = move->piece & PIECE_MASK;
125 uint8_t color = move->piece & COLOR_MASK; 95 uint8_t color = move->piece & COLOR_MASK;
126 96
127 /* en passant capture */ 97 /* en passant capture */
165 } 135 }
166 136
167 addmove(gamestate, move); 137 addmove(gamestate, move);
168 } 138 }
169 139
170 _Bool validate_move(GameState *gamestate, Move *move) { 140 static _Bool validate_move_rules(GameState *gamestate, Move *move) {
171 _Bool result;
172
173 /* validate indices (don't trust opponent) */ 141 /* validate indices (don't trust opponent) */
174 if (!chkidx(move)) { 142 if (!chkidx(move)) {
175 return 0; 143 return 0;
176 } 144 }
177 145
179 if (move->fromfile == move->tofile && move->fromrow == move->torow) { 147 if (move->fromfile == move->tofile && move->fromrow == move->torow) {
180 return 0; 148 return 0;
181 } 149 }
182 150
183 /* does piece exist */ 151 /* does piece exist */
184 result = msrc(gamestate->board, move) == move->piece; 152 if (msrc(gamestate->board, move) != move->piece) {
153 return 0;
154 }
185 155
186 /* can't capture own pieces */ 156 /* can't capture own pieces */
187 uint8_t piececolor = (move->piece & COLOR_MASK); 157 if ((mdst(gamestate->board, move) & COLOR_MASK)
188 if ((mdst(gamestate->board, move) & COLOR_MASK) == piececolor) { 158 == (move->piece & COLOR_MASK)) {
189 return 0; 159 return 0;
190 } 160 }
191 161
192 /* validate individual rules */ 162 /* validate individual rules */
193 switch (move->piece & PIECE_MASK) { 163 switch (move->piece & PIECE_MASK) {
194 case PAWN: 164 case PAWN:
195 result = result && pawn_chkrules(gamestate, move); 165 return pawn_chkrules(gamestate, move) &&
196 result = result && !pawn_isblocked(gamestate, move); 166 !pawn_isblocked(gamestate, move);
197 break;
198 case ROOK: 167 case ROOK:
199 result = result && rook_chkrules(move); 168 return rook_chkrules(move) &&
200 result = result && !rook_isblocked(gamestate, move); 169 !rook_isblocked(gamestate, move);
201 break;
202 case KNIGHT: 170 case KNIGHT:
203 result = result && knight_chkrules(move); 171 return knight_chkrules(move); /* knight is never blocked */
204 result = result && !knight_isblocked(gamestate, move);
205 break;
206 case BISHOP: 172 case BISHOP:
207 result = result && bishop_chkrules(move); 173 return bishop_chkrules(move) &&
208 result = result && !bishop_isblocked(gamestate, move); 174 !bishop_isblocked(gamestate, move);
209 break;
210 case QUEEN: 175 case QUEEN:
211 result = result && queen_chkrules(move); 176 return queen_chkrules(move) &&
212 result = result && !queen_isblocked(gamestate, move); 177 !queen_isblocked(gamestate, move);
213 break;
214 case KING: 178 case KING:
215 result = result && king_chkrules(gamestate, move); 179 return king_chkrules(gamestate, move) &&
216 result = result && !king_isblocked(gamestate, move); 180 !king_isblocked(gamestate, move);
217 break;
218 default: 181 default:
219 result = 0; 182 return 0;
220 } 183 }
221 184 }
222 /* cancel processing to avoid recursion overflow with is_covered() */ 185
186 _Bool validate_move(GameState *gamestate, Move *move) {
187
188 _Bool result = validate_move_rules(gamestate, move);
189
190 /* cancel processing to save resources */
223 if (!result) { 191 if (!result) {
224 return 0; 192 return 0;
225 } 193 }
226 194
227 /* find kings for check validation */ 195 /* find kings for check validation */
196 uint8_t piececolor = (move->piece & COLOR_MASK);
197
228 uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0; 198 uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0;
229 for (uint8_t row = 0 ; row < 8 ; row++) { 199 for (uint8_t row = 0 ; row < 8 ; row++) {
230 for (uint8_t file = 0 ; file < 8 ; file++) { 200 for (uint8_t file = 0 ; file < 8 ; file++) {
231 if (gamestate->board[row][file] == 201 if (gamestate->board[row][file] ==
232 (piececolor == WHITE?WKING:BKING)) { 202 (piececolor == WHITE?WKING:BKING)) {
250 opponent_color(piececolor))) { 220 opponent_color(piececolor))) {
251 return 0; 221 return 0;
252 } 222 }
253 223
254 /* correct check and checkmate flags (move is still valid) */ 224 /* correct check and checkmate flags (move is still valid) */
255 Move threat; 225 Move threats[16];
256 move->check = get_any_threat_for(&simulation, opkingrow, opkingfile, 226 uint8_t threatcount;
257 piececolor, &threat); 227 move->check = get_threats(&simulation, opkingrow, opkingfile,
228 piececolor, threats, &threatcount);
258 229
259 if (move->check) { 230 if (move->check) {
260 /* determine possible escape fields */ 231 /* determine possible escape fields */
261 _Bool canescape = 0; 232 _Bool canescape = 0;
262 for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { 233 for (int dr = -1 ; dr <= 1 && !canescape ; dr++) {
271 opkingrow + dr, opkingfile + df, piececolor); 242 opkingrow + dr, opkingfile + df, piececolor);
272 } 243 }
273 } 244 }
274 } 245 }
275 } 246 }
276 /* can't escape, can we capture? */ 247 /* can't escape, can he capture? */
248 if (!canescape && threatcount == 1) {
249 canescape = is_attacked(&simulation, threats[0].fromrow,
250 threats[0].fromfile, opponent_color(piececolor));
251 }
252
253 /* can't capture, can he block? */
254 if (!canescape && threatcount == 1) {
255 Move *threat = &(threats[0]);
256 uint8_t threatpiece = threat->piece & PIECE_MASK;
257
258 /* knight, pawns and the king cannot be blocked */
259 if (threatpiece == BISHOP || threatpiece == ROOK
260 || threatpiece == QUEEN) {
261 if (threat->fromrow == threat->torow) {
262 /* rook aspect (on row) */
263 int d = threat->tofile > threat->fromfile ? 1 : -1;
264 uint8_t file = threat->fromfile;
265 while (!canescape && file != threat->tofile - d) {
266 file += d;
267 canescape |= is_protected(&simulation,
268 threat->torow, file, opponent_color(piececolor));
269 }
270 } else if (threat->fromfile == threat->tofile) {
271 /* rook aspect (on file) */
272 int d = threat->torow > threat->fromrow ? 1 : -1;
273 uint8_t row = threat->fromrow;
274 while (!canescape && row != threat->torow - d) {
275 row += d;
276 canescape |= is_protected(&simulation,
277 row, threat->tofile, opponent_color(piececolor));
278 }
279 } else {
280 /* bishop aspect */
281 int dr = move->torow > move->fromrow ? 1 : -1;
282 int df = move->tofile > move->fromfile ? 1 : -1;
283
284 uint8_t row = move->fromrow;
285 uint8_t file = move->fromfile;
286 while (!canescape && file != move->tofile - df
287 && row != move->torow - dr) {
288 row += dr;
289 file += df;
290 canescape |= is_protected(&simulation, row, file,
291 opponent_color(piececolor));
292 }
293 }
294 }
295 }
296
277 if (!canescape) { 297 if (!canescape) {
278 canescape = is_covered(&simulation, threat.fromrow, 298 gamestate->checkmate = 1;
279 threat.fromfile, opponent_color(piececolor));
280
281 /* can't capture, can we block? */
282 // TODO: make it so
283
284 if (!canescape) {
285 gamestate->checkmate = 1;
286 }
287 } 299 }
288 } 300 }
289 301
290 return 1; 302 return 1;
291 } 303 }
416 } 428 }
417 } else { 429 } else {
418 return INVALID_MOVE_SYNTAX; 430 return INVALID_MOVE_SYNTAX;
419 } 431 }
420 } 432 }
433
434 _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
435 uint8_t color, Move *threats, uint8_t *threatcount) {
436 Move candidates[16];
437 int candidatecount = 0;
438 for (uint8_t r = 0 ; r < 8 ; r++) {
439 for (uint8_t f = 0 ; f < 8 ; f++) {
440 if ((gamestate->board[r][f] & COLOR_MASK) == color) {
441 memset(&(candidates[candidatecount]), 0, sizeof(Move));
442 candidates[candidatecount].piece = gamestate->board[r][f];
443 candidates[candidatecount].fromrow = r;
444 candidates[candidatecount].fromfile = f;
445 candidates[candidatecount].torow = row;
446 candidates[candidatecount].tofile = file;
447 candidatecount++;
448 }
449 }
450 }
451
452 if (threatcount) {
453 *threatcount = 0;
454 }
455
456
457 _Bool result = 0;
458
459 for (int i = 0 ; i < candidatecount ; i++) {
460 if (validate_move_rules(gamestate, &(candidates[i]))) {
461 result = 1;
462 if (threats && threatcount) {
463 threats[(*threatcount)++] = candidates[i];
464 }
465 }
466 }
467
468 return result;
469 }
470
471 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
472 uint8_t color, Move *threats, uint8_t *threatcount) {
473
474 Move candidates[16];
475 uint8_t candidatecount;
476 if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) {
477
478 if (threatcount) {
479 *threatcount = 0;
480 }
481 _Bool result = 0;
482 uint8_t kingfile = 0, kingrow = 0;
483 for (uint8_t row = 0 ; row < 8 ; row++) {
484 for (uint8_t file = 0 ; file < 8 ; file++) {
485 if ((gamestate->board[row][file] & COLOR_MASK) == color) {
486 kingfile = file;
487 kingrow = row;
488 }
489 }
490 }
491
492 for (uint8_t i = 0 ; i < candidatecount ; i++) {
493 GameState simulation;
494 memcpy(&simulation, gamestate, sizeof(GameState));
495 apply_move(&simulation, &(candidates[i]));
496 if (!is_covered(&simulation, kingrow, kingfile,
497 opponent_color(color))) {
498 result = 1;
499 if (threats && threatcount) {
500 threats[(*threatcount)++] = candidates[i];
501 }
502 }
503 }
504
505 return result;
506 } else {
507 return 0;
508 }
509 }
510
511 _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file,
512 uint8_t color) {
513
514 Move threats[16];
515 uint8_t threatcount;
516 if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) {
517 for (int i = 0 ; i < threatcount ; i++) {
518 if (threats[i].piece != (color|KING)) {
519 return 1;
520 }
521 }
522 return 0;
523 } else {
524 return 0;
525 }
526 }

mercurial