src/chess/rules.c

changeset 47
d726e4b46c33
parent 40
47162a7621da
child 48
0cedda2544da
equal deleted inserted replaced
46:4dcfb4c58b6d 47:d726e4b46c33
96 case 'K': return KING; 96 case 'K': return KING;
97 default: return 0; 97 default: return 0;
98 } 98 }
99 } 99 }
100 100
101 static int getlocation(GameState *gamestate, Move *move) {
102 uint8_t piece = move->piece & PIECE_MASK;
103 switch (piece) {
104 case PAWN: return pawn_getlocation(gamestate, move);
105 case ROOK: return rook_getlocation(gamestate, move);
106 case KNIGHT: return knight_getlocation(gamestate, move);
107 case BISHOP: return bishop_getlocation(gamestate, move);
108 case QUEEN: return queen_getlocation(gamestate, move);
109 case KING: return king_getlocation(gamestate, move);
110 default: return INVALID_MOVE_SYNTAX;
111 }
112 }
113
114 void apply_move(GameState *gamestate, Move *move) { 101 void apply_move(GameState *gamestate, Move *move) {
115 uint8_t piece = move->piece & PIECE_MASK; 102 uint8_t piece = move->piece & PIECE_MASK;
116 uint8_t color = move->piece & COLOR_MASK; 103 uint8_t color = move->piece & COLOR_MASK;
117 104
118 /* en passant capture */ 105 /* en passant capture */
168 if (move->fromfile == move->tofile && move->fromrow == move->torow) { 155 if (move->fromfile == move->tofile && move->fromrow == move->torow) {
169 return 0; 156 return 0;
170 } 157 }
171 158
172 /* does piece exist */ 159 /* does piece exist */
173 if (msrc(gamestate->board, move) != move->piece) { 160 if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK))
161 != (move->piece&(PIECE_MASK|COLOR_MASK))) {
174 return 0; 162 return 0;
175 } 163 }
176 164
177 /* can't capture own pieces */ 165 /* can't capture own pieces */
178 if ((mdst(gamestate->board, move) & COLOR_MASK) 166 if ((mdst(gamestate->board, move) & COLOR_MASK)
179 == (move->piece & COLOR_MASK)) { 167 == (move->piece & COLOR_MASK)) {
168 return 0;
169 }
170
171 /* must capture, if and only if destination is occupied */
172 if ((mdst(gamestate->board, move) == 0 && move->capture) ||
173 (mdst(gamestate->board, move) != 0 && !move->capture)) {
180 return 0; 174 return 0;
181 } 175 }
182 176
183 /* validate individual rules */ 177 /* validate individual rules */
184 switch (move->piece & PIECE_MASK) { 178 switch (move->piece & PIECE_MASK) {
203 return 0; 197 return 0;
204 } 198 }
205 } 199 }
206 200
207 _Bool validate_move(GameState *gamestate, Move *move) { 201 _Bool validate_move(GameState *gamestate, Move *move) {
202 // TODO: provide more details via a return code
208 203
209 _Bool result = validate_move_rules(gamestate, move); 204 _Bool result = validate_move_rules(gamestate, move);
210 205
211 /* cancel processing to save resources */ 206 /* cancel processing to save resources */
212 if (!result) { 207 if (!result) {
230 } 225 }
231 } 226 }
232 } 227 }
233 228
234 /* simulation move for check validation */ 229 /* simulation move for check validation */
235 GameState simulation; 230 GameState simulation = *gamestate;
236 memcpy(&simulation, gamestate, sizeof(GameState)); 231 Move simmove = *move;
237 apply_move(&simulation, move); 232 apply_move(&simulation, &simmove);
238 233
239 /* don't move into or stay in check position */ 234 /* don't move into or stay in check position */
240 if (is_covered(&simulation, mykingrow, mykingfile, 235 if (is_covered(&simulation, mykingrow, mykingfile,
241 opponent_color(piececolor))) { 236 opponent_color(piececolor))) {
242 return 0; 237 return 0;
321 } 316 }
322 317
323 return 1; 318 return 1;
324 } 319 }
325 320
321 _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
322 uint8_t color, Move *threats, uint8_t *threatcount) {
323 Move candidates[32];
324 int candidatecount = 0;
325 for (uint8_t r = 0 ; r < 8 ; r++) {
326 for (uint8_t f = 0 ; f < 8 ; f++) {
327 if ((gamestate->board[r][f] & COLOR_MASK) == color) {
328 // non-capturing move
329 memset(&(candidates[candidatecount]), 0, sizeof(Move));
330 candidates[candidatecount].piece = gamestate->board[r][f];
331 candidates[candidatecount].fromrow = r;
332 candidates[candidatecount].fromfile = f;
333 candidates[candidatecount].torow = row;
334 candidates[candidatecount].tofile = file;
335 candidatecount++;
336
337 // capturing move
338 memcpy(&(candidates[candidatecount]),
339 &(candidates[candidatecount-1]), sizeof(Move));
340 candidates[candidatecount].capture = 1;
341 candidatecount++;
342 }
343 }
344 }
345
346 if (threatcount) {
347 *threatcount = 0;
348 }
349
350
351 _Bool result = 0;
352
353 for (int i = 0 ; i < candidatecount ; i++) {
354 if (validate_move_rules(gamestate, &(candidates[i]))) {
355 result = 1;
356 if (threats && threatcount) {
357 threats[(*threatcount)++] = candidates[i];
358 }
359 }
360 }
361
362 return result;
363 }
364
365 _Bool is_pinned(GameState *gamestate, Move *move) {
366 uint8_t color = move->piece & COLOR_MASK;
367
368 uint8_t kingfile = 0, kingrow = 0;
369 for (uint8_t row = 0 ; row < 8 ; row++) {
370 for (uint8_t file = 0 ; file < 8 ; file++) {
371 if (gamestate->board[row][file] == (color|KING)) {
372 kingfile = file;
373 kingrow = row;
374 }
375 }
376 }
377
378 GameState simulation = *gamestate;
379 Move simmove = *move;
380 apply_move(&simulation, &simmove);
381 return is_covered(&simulation, kingrow, kingfile, opponent_color(color));
382 }
383
384 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
385 uint8_t color, Move *threats, uint8_t *threatcount) {
386
387 if (threatcount) {
388 *threatcount = 0;
389 }
390
391 Move candidates[16];
392 uint8_t candidatecount;
393 if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) {
394
395 _Bool result = 0;
396 uint8_t kingfile = 0, kingrow = 0;
397 for (uint8_t row = 0 ; row < 8 ; row++) {
398 for (uint8_t file = 0 ; file < 8 ; file++) {
399 if (gamestate->board[row][file] == (color|KING)) {
400 kingfile = file;
401 kingrow = row;
402 }
403 }
404 }
405
406 for (uint8_t i = 0 ; i < candidatecount ; i++) {
407 GameState simulation = *gamestate;
408 Move simmove = candidates[i];
409 apply_move(&simulation, &simmove);
410 if (!is_covered(&simulation, kingrow, kingfile,
411 opponent_color(color))) {
412 result = 1;
413 if (threats && threatcount) {
414 threats[(*threatcount)++] = candidates[i];
415 }
416 }
417 }
418
419 return result;
420 } else {
421 return 0;
422 }
423 }
424 #include <ncurses.h>
425 static int getlocation(GameState *gamestate, Move *move) {
426
427 uint8_t color = move->piece & COLOR_MASK;
428 _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0;
429
430 Move threats[16], *threat = NULL;
431 uint8_t threatcount;
432
433 if (get_threats(gamestate, move->torow, move->tofile, color,
434 threats, &threatcount)) {
435
436 // find threats for the specified position
437 for (uint8_t i = 0 ; i < threatcount ; i++) {
438 if ((threats[i].piece & (PIECE_MASK | COLOR_MASK))
439 == move->piece &&
440 (move->fromrow == POS_UNSPECIFIED ||
441 move->fromrow == threats[i].fromrow) &&
442 (move->fromfile == POS_UNSPECIFIED ||
443 move->fromfile == threats[i].fromfile)) {
444
445 if (threat) {
446 return AMBIGUOUS_MOVE;
447 } else {
448 threat = &(threats[i]);
449 }
450 }
451 }
452
453 // can't threaten specified position
454 if (!threat) {
455 return INVALID_POSITION;
456 }
457
458 // found threat is no real threat
459 if (is_pinned(gamestate, threat)) {
460 return incheck?KING_IN_CHECK:PIECE_PINNED;
461 } else {
462 memcpy(move, threat, sizeof(Move));
463 return VALID_MOVE_SYNTAX;
464 }
465 } else {
466 return INVALID_POSITION;
467 }
468 }
469
326 int eval_move(GameState *gamestate, char *mstr, Move *move) { 470 int eval_move(GameState *gamestate, char *mstr, Move *move) {
327 memset(move, 0, sizeof(Move)); 471 memset(move, 0, sizeof(Move));
328 move->fromfile = POS_UNSPECIFIED; 472 move->fromfile = POS_UNSPECIFIED;
329 move->fromrow = POS_UNSPECIFIED; 473 move->fromrow = POS_UNSPECIFIED;
330 474
450 } else { 594 } else {
451 return INVALID_MOVE_SYNTAX; 595 return INVALID_MOVE_SYNTAX;
452 } 596 }
453 } 597 }
454 598
455 _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
456 uint8_t color, Move *threats, uint8_t *threatcount) {
457 Move candidates[16];
458 int candidatecount = 0;
459 for (uint8_t r = 0 ; r < 8 ; r++) {
460 for (uint8_t f = 0 ; f < 8 ; f++) {
461 if ((gamestate->board[r][f] & COLOR_MASK) == color) {
462 memset(&(candidates[candidatecount]), 0, sizeof(Move));
463 candidates[candidatecount].piece = gamestate->board[r][f];
464 candidates[candidatecount].fromrow = r;
465 candidates[candidatecount].fromfile = f;
466 candidates[candidatecount].torow = row;
467 candidates[candidatecount].tofile = file;
468 candidatecount++;
469 }
470 }
471 }
472
473 if (threatcount) {
474 *threatcount = 0;
475 }
476
477
478 _Bool result = 0;
479
480 for (int i = 0 ; i < candidatecount ; i++) {
481 if (validate_move_rules(gamestate, &(candidates[i]))) {
482 result = 1;
483 if (threats && threatcount) {
484 threats[(*threatcount)++] = candidates[i];
485 }
486 }
487 }
488
489 return result;
490 }
491
492 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
493 uint8_t color, Move *threats, uint8_t *threatcount) {
494
495 Move candidates[16];
496 uint8_t candidatecount;
497 if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) {
498
499 if (threatcount) {
500 *threatcount = 0;
501 }
502 _Bool result = 0;
503 uint8_t kingfile = 0, kingrow = 0;
504 for (uint8_t row = 0 ; row < 8 ; row++) {
505 for (uint8_t file = 0 ; file < 8 ; file++) {
506 if ((gamestate->board[row][file] & COLOR_MASK) == color) {
507 kingfile = file;
508 kingrow = row;
509 }
510 }
511 }
512
513 for (uint8_t i = 0 ; i < candidatecount ; i++) {
514 GameState simulation;
515 memcpy(&simulation, gamestate, sizeof(GameState));
516 apply_move(&simulation, &(candidates[i]));
517 if (!is_covered(&simulation, kingrow, kingfile,
518 opponent_color(color))) {
519 result = 1;
520 if (threats && threatcount) {
521 threats[(*threatcount)++] = candidates[i];
522 }
523 }
524 }
525
526 return result;
527 } else {
528 return 0;
529 }
530 }
531
532 _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file, 599 _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file,
533 uint8_t color) { 600 uint8_t color) {
534 601
535 Move threats[16]; 602 Move threats[16];
536 uint8_t threatcount; 603 uint8_t threatcount;

mercurial