Wed, 11 Jun 2014 15:38:01 +0200
added return code to move validation (for more informative messages) + fixed a bug where simulations added movelist items to the original gamestate
src/chess/rules.c | file | annotate | diff | comparison | revisions | |
src/chess/rules.h | file | annotate | diff | comparison | revisions | |
src/game.c | file | annotate | diff | comparison | revisions | |
src/network.c | file | annotate | diff | comparison | revisions | |
src/network.h | file | annotate | diff | comparison | revisions |
--- 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) {
--- a/src/chess/rules.h Wed May 28 15:47:57 2014 +0200 +++ b/src/chess/rules.h Wed Jun 11 15:38:01 2014 +0200 @@ -33,13 +33,16 @@ #include <stdint.h> #include <sys/time.h> -#define VALID_MOVE_SYNTAX 0 -#define INVALID_MOVE_SYNTAX 1 -#define INVALID_POSITION 2 -#define AMBIGUOUS_MOVE 3 -#define NEED_PROMOTION 4 -#define PIECE_PINNED 5 -#define KING_IN_CHECK 6 +#define VALID_MOVE_SYNTAX 0 +#define VALID_MOVE_SEMANTICS 0 /* use same code for a success */ +#define INVALID_MOVE_SYNTAX 1 +#define INVALID_POSITION 2 +#define AMBIGUOUS_MOVE 3 +#define NEED_PROMOTION 4 +#define PIECE_PINNED 5 +#define KING_IN_CHECK 6 +#define KING_MOVES_INTO_CHECK 7 +#define RULES_VIOLATED 10 #define PIECE_MASK 0x0F @@ -268,7 +271,7 @@ * @param gamestate the current game state * @param mstr the input string to parse * @param move a pointer to object where the move data shall be stored - * @return status code (see rules/rules.h for the list of codes) + * @return status code (see macros in this file for the list of codes) */ int eval_move(GameState *gamestate, char *mstr, Move *move); @@ -276,9 +279,9 @@ * Validates move by applying chess rules. * @param gamestate the current game state * @param move the move to validate - * @return TRUE, if the move complies to chess rules, FALSE otherwise + * @return status code (see macros in this file for the list of codes) */ -_Bool validate_move(GameState *gamestate, Move *move); +int validate_move(GameState *gamestate, Move *move); /** * Applies a move and deletes captured pieces.
--- a/src/game.c Wed May 28 15:47:57 2014 +0200 +++ b/src/game.c Wed Jun 11 15:38:01 2014 +0200 @@ -162,7 +162,7 @@ printw("Ambiguous move - please specify the piece to move."); break; case INVALID_POSITION: - printw("Cannot find the piece that shall be moved."); + printw("No piece can be moved this way."); break; case NEED_PROMOTION: printw("You need to promote the pawn (append \"=Q\" e.g.)!"); @@ -176,6 +176,12 @@ case INVALID_MOVE_SYNTAX: printw("Can't interpret move - please use algebraic notation."); break; + case RULES_VIOLATED: + printw("Move does not comply chess rules."); + break; + case KING_MOVES_INTO_CHECK: + printw("Can't move the king into a check position."); + break; default: printw("Unknown move parser error."); } @@ -218,7 +224,8 @@ int eval_result = eval_move(gamestate, movestr, &move); switch (eval_result) { case VALID_MOVE_SYNTAX: - if (validate_move(gamestate, &move)) { + eval_result = validate_move(gamestate, &move); + if (eval_result == VALID_MOVE_SEMANTICS) { apply_move(gamestate, &move); if (gamestate->checkmate) { printw("Checkmate!"); @@ -232,7 +239,7 @@ return 0; } } else { - printw("Invalid move."); + eval_move_failed_msg(eval_result); } break; default: @@ -306,11 +313,15 @@ case VALID_MOVE_SYNTAX: net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move)); code = net_recieve_code(opponent); - move.check = code == NETCODE_CHECK; + move.check = code == NETCODE_CHECK || + code == NETCODE_CHECKMATE; gamestate->checkmate = code == NETCODE_CHECKMATE; gamestate->stalemate = code == NETCODE_STALEMATE; if (code == NETCODE_DECLINE) { - printw("Invalid move."); + uint32_t reason; + net_recieve_data(opponent, &reason, sizeof(uint32_t)); + reason = ntohl(reason); + eval_move_failed_msg(reason); } else if (code == NETCODE_ACCEPT || code == NETCODE_CHECK || code == NETCODE_CHECKMATE @@ -397,7 +408,8 @@ break; case NETCODE_MOVE: net_recieve_data(opponent, &move, sizeof(Move)); - if (validate_move(gamestate, &move)) { + code = validate_move(gamestate, &move); + if (code == VALID_MOVE_SEMANTICS) { apply_move(gamestate, &move); if (move.check) { net_send_code(opponent, NETCODE_CHECK); @@ -416,7 +428,9 @@ } return 0; } else { - net_send_code(opponent, NETCODE_DECLINE); + uint32_t reason = htonl(code); + net_send_data(opponent, NETCODE_DECLINE, + &reason, sizeof(uint32_t)); } break; default:
--- a/src/network.c Wed May 28 15:47:57 2014 +0200 +++ b/src/network.c Wed Jun 11 15:38:01 2014 +0200 @@ -56,7 +56,7 @@ } } -int getaddrinfo_intrnl(char *host, char *port, struct addrinfo **info) { +static int getaddrinfo_intrnl(char *host, char *port, struct addrinfo **info) { struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM;