--- a/src/chess/rules.c Wed May 28 15:47:57 2014 +0200 +++ b/src/chess/rules.c Wed Jun 11 15:38:01 2014 +0200 @@ -33,6 +33,17 @@ #include <stdlib.h> #include <sys/time.h> +static GameState gamestate_copy_sim(GameState *gamestate) { + GameState simulation = *gamestate; + if (simulation.lastmove) { + MoveList *lastmovecopy = malloc(sizeof(MoveList)); + *lastmovecopy = *(simulation.lastmove); + simulation.movelist = simulation.lastmove = lastmovecopy; + } + + return simulation; +} + void gamestate_cleanup(GameState *gamestate) { MoveList *elem; elem = gamestate->movelist; @@ -145,67 +156,75 @@ addmove(gamestate, move); } -static _Bool validate_move_rules(GameState *gamestate, Move *move) { +static int validate_move_rules(GameState *gamestate, Move *move) { /* validate indices (don't trust opponent) */ if (!chkidx(move)) { - return 0; + return INVALID_POSITION; } /* must move */ if (move->fromfile == move->tofile && move->fromrow == move->torow) { - return 0; + return INVALID_MOVE_SYNTAX; } /* does piece exist */ if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK)) != (move->piece&(PIECE_MASK|COLOR_MASK))) { - return 0; + return INVALID_POSITION; } /* can't capture own pieces */ if ((mdst(gamestate->board, move) & COLOR_MASK) == (move->piece & COLOR_MASK)) { - return 0; + return RULES_VIOLATED; } /* must capture, if and only if destination is occupied */ if ((mdst(gamestate->board, move) == 0 && move->capture) || (mdst(gamestate->board, move) != 0 && !move->capture)) { - return 0; + return INVALID_MOVE_SYNTAX; } /* validate individual rules */ + _Bool chkrules; switch (move->piece & PIECE_MASK) { case PAWN: - return pawn_chkrules(gamestate, move) && + chkrules = pawn_chkrules(gamestate, move) && !pawn_isblocked(gamestate, move); + break; case ROOK: - return rook_chkrules(move) && + chkrules = rook_chkrules(move) && !rook_isblocked(gamestate, move); + break; case KNIGHT: - return knight_chkrules(move); /* knight is never blocked */ + chkrules = knight_chkrules(move); /* knight is never blocked */ + break; case BISHOP: - return bishop_chkrules(move) && + chkrules = bishop_chkrules(move) && !bishop_isblocked(gamestate, move); + break; case QUEEN: - return queen_chkrules(move) && + chkrules = queen_chkrules(move) && !queen_isblocked(gamestate, move); + break; case KING: - return king_chkrules(gamestate, move) && + chkrules = king_chkrules(gamestate, move) && !king_isblocked(gamestate, move); + break; default: - return 0; + return INVALID_MOVE_SYNTAX; } + + return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED; } -_Bool validate_move(GameState *gamestate, Move *move) { - // TODO: provide more details via a return code +int validate_move(GameState *gamestate, Move *move) { - _Bool result = validate_move_rules(gamestate, move); + int result = validate_move_rules(gamestate, move); /* cancel processing to save resources */ - if (!result) { - return 0; + if (result != VALID_MOVE_SEMANTICS) { + return result; } /* find kings for check validation */ @@ -226,15 +245,23 @@ } } - /* simulation move for check validation */ - GameState simulation = *gamestate; + /* simulate move for check validation */ + GameState simulation = gamestate_copy_sim(gamestate); Move simmove = *move; apply_move(&simulation, &simmove); /* don't move into or stay in check position */ if (is_covered(&simulation, mykingrow, mykingfile, opponent_color(piececolor))) { - return 0; + + gamestate_cleanup(&simulation); + if ((move->piece & PIECE_MASK) == KING) { + return KING_MOVES_INTO_CHECK; + } else { + /* last move is always not null in this case */ + return gamestate->lastmove->move.check ? + KING_IN_CHECK : PIECE_PINNED; + } } /* correct check and checkmate flags (move is still valid) */ @@ -315,7 +342,9 @@ } } - return 1; + gamestate_cleanup(&simulation); + + return VALID_MOVE_SEMANTICS; } _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, @@ -351,7 +380,8 @@ _Bool result = 0; for (int i = 0 ; i < candidatecount ; i++) { - if (validate_move_rules(gamestate, &(candidates[i]))) { + if (validate_move_rules(gamestate, &(candidates[i])) + == VALID_MOVE_SEMANTICS) { result = 1; if (threats && threatcount) { threats[(*threatcount)++] = candidates[i]; @@ -375,10 +405,14 @@ } } - GameState simulation = *gamestate; + GameState simulation = gamestate_copy_sim(gamestate); Move simmove = *move; apply_move(&simulation, &simmove); - return is_covered(&simulation, kingrow, kingfile, opponent_color(color)); + _Bool covered = is_covered(&simulation, + kingrow, kingfile, opponent_color(color)); + gamestate_cleanup(&simulation); + + return covered; } _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, @@ -404,7 +438,7 @@ } for (uint8_t i = 0 ; i < candidatecount ; i++) { - GameState simulation = *gamestate; + GameState simulation = gamestate_copy_sim(gamestate); Move simmove = candidates[i]; apply_move(&simulation, &simmove); if (!is_covered(&simulation, kingrow, kingfile, @@ -414,6 +448,7 @@ threats[(*threatcount)++] = candidates[i]; } } + gamestate_cleanup(&simulation); } return result; @@ -421,7 +456,7 @@ return 0; } } -#include <ncurses.h> + static int getlocation(GameState *gamestate, Move *move) { uint8_t color = move->piece & COLOR_MASK; @@ -473,6 +508,9 @@ move->fromrow = POS_UNSPECIFIED; size_t len = strlen(mstr); + if (len < 1 || len > 6) { + return INVALID_MOVE_SYNTAX; + } /* evaluate check/checkmate flags */ if (mstr[len-1] == '+') { @@ -513,7 +551,6 @@ move->tofile = fileidx(mstr[1]); move->torow = rowidx(mstr[2]); } - } else if (len == 4) { move->piece = getpiece(mstr[0]); if (!move->piece) {