1.1 --- a/src/chess/rules.c Wed Jun 11 15:38:01 2014 +0200 1.2 +++ b/src/chess/rules.c Wed Jun 11 16:54:20 2014 +0200 1.3 @@ -54,6 +54,81 @@ 1.4 }; 1.5 } 1.6 1.7 +/* MUST be called IMMEDIATLY after applying a move to work correctly */ 1.8 +static void format_move(GameState *gamestate, Move *move) { 1.9 + char *string = move->string; 1.10 + 1.11 + /* at least 8 characters should be available, wipe them out */ 1.12 + memset(string, 0, 8); 1.13 + 1.14 + /* special formats for castling */ 1.15 + if ((move->piece&PIECE_MASK) == KING && 1.16 + abs(move->tofile-move->fromfile) == 2) { 1.17 + if (move->tofile==fileidx('c')) { 1.18 + memcpy(string, "O-O-O", 5); 1.19 + } else { 1.20 + memcpy(string, "O-O", 3); 1.21 + } 1.22 + } 1.23 + 1.24 + /* start by notating the piece character */ 1.25 + string[0] = getpiecechr(move->piece); 1.26 + int idx = string[0] ? 1 : 0; 1.27 + 1.28 + /* find out how many source information we do need */ 1.29 + uint8_t piece = move->piece & PIECE_MASK; 1.30 + if (piece == PAWN) { 1.31 + if (move->capture) { 1.32 + string[idx++] = filechr(move->fromfile); 1.33 + } 1.34 + } else if (piece != KING) { 1.35 + Move threats[16]; 1.36 + uint8_t threatcount; 1.37 + get_real_threats(gamestate, move->torow, move->tofile, 1.38 + move->piece&COLOR_MASK, threats, &threatcount); 1.39 + if (threatcount > 1) { 1.40 + int ambrows = 0, ambfiles = 0; 1.41 + for (uint8_t i = 0 ; i < threatcount ; i++) { 1.42 + if (threats[i].fromrow == move->fromrow) { 1.43 + ambrows++; 1.44 + } 1.45 + if (threats[i].fromfile == move->fromfile) { 1.46 + ambfiles++; 1.47 + } 1.48 + } 1.49 + /* ambiguous row, name file */ 1.50 + if (ambrows > 1) { 1.51 + string[idx++] = filechr(move->fromfile); 1.52 + } 1.53 + /* ambiguous file, name row */ 1.54 + if (ambfiles > 1) { 1.55 + string[idx++] = filechr(move->fromrow); 1.56 + } 1.57 + } 1.58 + } 1.59 + 1.60 + /* capturing? */ 1.61 + if (move->capture) { 1.62 + string[idx++] = 'x'; 1.63 + } 1.64 + 1.65 + /* destination */ 1.66 + string[idx++] = filechr(move->tofile); 1.67 + string[idx++] = rowchr(move->torow); 1.68 + 1.69 + /* promotion? */ 1.70 + if (move->promotion) { 1.71 + string[idx++] = '='; 1.72 + string[idx++] = getpiecechr(move->promotion); 1.73 + } 1.74 + 1.75 + /* check? */ 1.76 + if (move->check) { 1.77 + /* works only, if this function is called when applying the move */ 1.78 + string[idx++] = gamestate->checkmate?'#':'+'; 1.79 + } 1.80 +} 1.81 + 1.82 static void addmove(GameState* gamestate, Move *move) { 1.83 MoveList *elem = malloc(sizeof(MoveList)); 1.84 elem->next = NULL; 1.85 @@ -109,7 +184,7 @@ 1.86 } 1.87 } 1.88 1.89 -void apply_move(GameState *gamestate, Move *move) { 1.90 +static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) { 1.91 uint8_t piece = move->piece & PIECE_MASK; 1.92 uint8_t color = move->piece & COLOR_MASK; 1.93 1.94 @@ -141,8 +216,7 @@ 1.95 } 1.96 1.97 /* castling */ 1.98 - if (piece == KING && 1.99 - move->fromfile == fileidx('e')) { 1.100 + if (piece == KING && move->fromfile == fileidx('e')) { 1.101 1.102 if (move->tofile == fileidx('g')) { 1.103 gamestate->board[move->torow][fileidx('h')] = 0; 1.104 @@ -153,9 +227,19 @@ 1.105 } 1.106 } 1.107 1.108 + if (!simulate) { 1.109 + if (!move->string[0]) { 1.110 + format_move(gamestate, move); 1.111 + } 1.112 + } 1.113 + /* add move, even in simulation (checkmate test needs it) */ 1.114 addmove(gamestate, move); 1.115 } 1.116 1.117 +void apply_move(GameState *gamestate, Move *move) { 1.118 + apply_move_impl(gamestate, move, 0); 1.119 +} 1.120 + 1.121 static int validate_move_rules(GameState *gamestate, Move *move) { 1.122 /* validate indices (don't trust opponent) */ 1.123 if (!chkidx(move)) { 1.124 @@ -248,7 +332,7 @@ 1.125 /* simulate move for check validation */ 1.126 GameState simulation = gamestate_copy_sim(gamestate); 1.127 Move simmove = *move; 1.128 - apply_move(&simulation, &simmove); 1.129 + apply_move_impl(&simulation, &simmove, 1); 1.130 1.131 /* don't move into or stay in check position */ 1.132 if (is_covered(&simulation, mykingrow, mykingfile, 1.133 @@ -448,7 +532,6 @@ 1.134 threats[(*threatcount)++] = candidates[i]; 1.135 } 1.136 } 1.137 - gamestate_cleanup(&simulation); 1.138 } 1.139 1.140 return result; 1.141 @@ -468,6 +551,8 @@ 1.142 if (get_threats(gamestate, move->torow, move->tofile, color, 1.143 threats, &threatcount)) { 1.144 1.145 + int reason = INVALID_POSITION; 1.146 + 1.147 // find threats for the specified position 1.148 for (uint8_t i = 0 ; i < threatcount ; i++) { 1.149 if ((threats[i].piece & (PIECE_MASK | COLOR_MASK)) 1.150 @@ -480,23 +565,23 @@ 1.151 if (threat) { 1.152 return AMBIGUOUS_MOVE; 1.153 } else { 1.154 - threat = &(threats[i]); 1.155 + // found threat is no real threat 1.156 + if (is_pinned(gamestate, &(threats[i]))) { 1.157 + reason = incheck?KING_IN_CHECK:PIECE_PINNED; 1.158 + } else { 1.159 + threat = &(threats[i]); 1.160 + } 1.161 } 1.162 } 1.163 } 1.164 1.165 // can't threaten specified position 1.166 if (!threat) { 1.167 - return INVALID_POSITION; 1.168 + return reason; 1.169 } 1.170 1.171 - // found threat is no real threat 1.172 - if (is_pinned(gamestate, threat)) { 1.173 - return incheck?KING_IN_CHECK:PIECE_PINNED; 1.174 - } else { 1.175 - memcpy(move, threat, sizeof(Move)); 1.176 - return VALID_MOVE_SYNTAX; 1.177 - } 1.178 + memcpy(move, threat, sizeof(Move)); 1.179 + return VALID_MOVE_SYNTAX; 1.180 } else { 1.181 return INVALID_POSITION; 1.182 }