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) { |
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; |