# HG changeset patch # User Mike Becker # Date 1402498460 -7200 # Node ID 02c509a44e98f5e2cc8963d7f6cf9ddef1faa40e # Parent 0cedda2544da3fa54933150946a6b9c27c284b40 logging string representation of moves in short algebraic notation diff -r 0cedda2544da -r 02c509a44e98 src/chess/rules.c --- a/src/chess/rules.c Wed Jun 11 15:38:01 2014 +0200 +++ b/src/chess/rules.c Wed Jun 11 16:54:20 2014 +0200 @@ -54,6 +54,81 @@ }; } +/* MUST be called IMMEDIATLY after applying a move to work correctly */ +static void format_move(GameState *gamestate, Move *move) { + char *string = move->string; + + /* at least 8 characters should be available, wipe them out */ + memset(string, 0, 8); + + /* special formats for castling */ + if ((move->piece&PIECE_MASK) == KING && + abs(move->tofile-move->fromfile) == 2) { + if (move->tofile==fileidx('c')) { + memcpy(string, "O-O-O", 5); + } else { + memcpy(string, "O-O", 3); + } + } + + /* start by notating the piece character */ + string[0] = getpiecechr(move->piece); + int idx = string[0] ? 1 : 0; + + /* find out how many source information we do need */ + uint8_t piece = move->piece & PIECE_MASK; + if (piece == PAWN) { + if (move->capture) { + string[idx++] = filechr(move->fromfile); + } + } else if (piece != KING) { + Move threats[16]; + uint8_t threatcount; + get_real_threats(gamestate, move->torow, move->tofile, + move->piece&COLOR_MASK, threats, &threatcount); + if (threatcount > 1) { + int ambrows = 0, ambfiles = 0; + for (uint8_t i = 0 ; i < threatcount ; i++) { + if (threats[i].fromrow == move->fromrow) { + ambrows++; + } + if (threats[i].fromfile == move->fromfile) { + ambfiles++; + } + } + /* ambiguous row, name file */ + if (ambrows > 1) { + string[idx++] = filechr(move->fromfile); + } + /* ambiguous file, name row */ + if (ambfiles > 1) { + string[idx++] = filechr(move->fromrow); + } + } + } + + /* capturing? */ + if (move->capture) { + string[idx++] = 'x'; + } + + /* destination */ + string[idx++] = filechr(move->tofile); + string[idx++] = rowchr(move->torow); + + /* promotion? */ + if (move->promotion) { + string[idx++] = '='; + string[idx++] = getpiecechr(move->promotion); + } + + /* check? */ + if (move->check) { + /* works only, if this function is called when applying the move */ + string[idx++] = gamestate->checkmate?'#':'+'; + } +} + static void addmove(GameState* gamestate, Move *move) { MoveList *elem = malloc(sizeof(MoveList)); elem->next = NULL; @@ -109,7 +184,7 @@ } } -void apply_move(GameState *gamestate, Move *move) { +static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) { uint8_t piece = move->piece & PIECE_MASK; uint8_t color = move->piece & COLOR_MASK; @@ -141,8 +216,7 @@ } /* castling */ - if (piece == KING && - move->fromfile == fileidx('e')) { + if (piece == KING && move->fromfile == fileidx('e')) { if (move->tofile == fileidx('g')) { gamestate->board[move->torow][fileidx('h')] = 0; @@ -153,9 +227,19 @@ } } + if (!simulate) { + if (!move->string[0]) { + format_move(gamestate, move); + } + } + /* add move, even in simulation (checkmate test needs it) */ addmove(gamestate, move); } +void apply_move(GameState *gamestate, Move *move) { + apply_move_impl(gamestate, move, 0); +} + static int validate_move_rules(GameState *gamestate, Move *move) { /* validate indices (don't trust opponent) */ if (!chkidx(move)) { @@ -248,7 +332,7 @@ /* simulate move for check validation */ GameState simulation = gamestate_copy_sim(gamestate); Move simmove = *move; - apply_move(&simulation, &simmove); + apply_move_impl(&simulation, &simmove, 1); /* don't move into or stay in check position */ if (is_covered(&simulation, mykingrow, mykingfile, @@ -448,7 +532,6 @@ threats[(*threatcount)++] = candidates[i]; } } - gamestate_cleanup(&simulation); } return result; @@ -468,6 +551,8 @@ if (get_threats(gamestate, move->torow, move->tofile, color, threats, &threatcount)) { + int reason = INVALID_POSITION; + // find threats for the specified position for (uint8_t i = 0 ; i < threatcount ; i++) { if ((threats[i].piece & (PIECE_MASK | COLOR_MASK)) @@ -480,23 +565,23 @@ if (threat) { return AMBIGUOUS_MOVE; } else { - threat = &(threats[i]); + // found threat is no real threat + if (is_pinned(gamestate, &(threats[i]))) { + reason = incheck?KING_IN_CHECK:PIECE_PINNED; + } else { + threat = &(threats[i]); + } } } } // can't threaten specified position if (!threat) { - return INVALID_POSITION; + return reason; } - // found threat is no real threat - if (is_pinned(gamestate, threat)) { - return incheck?KING_IN_CHECK:PIECE_PINNED; - } else { - memcpy(move, threat, sizeof(Move)); - return VALID_MOVE_SYNTAX; - } + memcpy(move, threat, sizeof(Move)); + return VALID_MOVE_SYNTAX; } else { return INVALID_POSITION; } diff -r 0cedda2544da -r 02c509a44e98 src/chess/rules.h --- a/src/chess/rules.h Wed Jun 11 15:38:01 2014 +0200 +++ b/src/chess/rules.h Wed Jun 11 16:54:20 2014 +0200 @@ -90,6 +90,7 @@ uint8_t capture; struct movetimeval timestamp; struct movetimeval movetime; + char string[8]; } Move; typedef struct MoveList MoveList; @@ -140,6 +141,10 @@ #define fileidx_s(c) (isfile(c)?fileidx(c):POS_UNSPECIFIED) #define rowidx_s(c) (isrow(c)?rowidx(c):POS_UNSPECIFIED) +/** + * Cleans up a game state and frees the memory for the movement list. + * @param gamestate the game state to clean up + */ void gamestate_cleanup(GameState *gamestate); /** diff -r 0cedda2544da -r 02c509a44e98 src/game.c --- a/src/game.c Wed Jun 11 15:38:01 2014 +0200 +++ b/src/game.c Wed Jun 11 16:54:20 2014 +0200 @@ -123,29 +123,9 @@ } if (logelem) { - Move move = logelem->move; - if ((move.piece&PIECE_MASK) == KING && - abs(move.tofile-move.fromfile) == 2) { - addstr(move.tofile==fileidx('c')?"O-O-O":"O-O"); - } else { - char logstr[] = { - getpiecechr(move.piece), - filechr(move.fromfile), rowchr(move.fromrow), - move.capture ? 'x':'\0', - filechr(move.tofile), rowchr(move.torow), - move.check ? '+' : (move.promotion ? '=' : '\0'), - move.promotion ? getpiecechr(move.promotion) : '\0' - }; - for (int stri = 0 ; stri < sizeof(logstr) ; stri++) { - if (logstr[stri]) { - addch(logstr[stri]); - } - } - } + addstr(logelem->move.string); if (!logelem->next) { - if (gamestate->checkmate) { - addstr("\b#"); - } else if (gamestate->stalemate) { + if (gamestate->stalemate) { addstr(" stalemate"); } } @@ -311,7 +291,8 @@ int eval_result = eval_move(gamestate, movestr, &move); switch (eval_result) { case VALID_MOVE_SYNTAX: - net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move)); + net_send_data(opponent, NETCODE_MOVE, &move, + sizeof(Move)-8); code = net_recieve_code(opponent); move.check = code == NETCODE_CHECK || code == NETCODE_CHECKMATE; @@ -407,7 +388,7 @@ } break; case NETCODE_MOVE: - net_recieve_data(opponent, &move, sizeof(Move)); + net_recieve_data(opponent, &move, sizeof(Move)-8); code = validate_move(gamestate, &move); if (code == VALID_MOVE_SEMANTICS) { apply_move(gamestate, &move); diff -r 0cedda2544da -r 02c509a44e98 src/network.h --- a/src/network.h Wed Jun 11 15:38:01 2014 +0200 +++ b/src/network.h Wed Jun 11 16:54:20 2014 +0200 @@ -49,7 +49,7 @@ #define NETCODE_TIMEOVER 0x44 #define NETCODE_CONNLOST 0x80 -#define NETCODE_VERSION 14 +#define NETCODE_VERSION 15 typedef struct { int fd; /* -1, if we are the client */