diff options
Diffstat (limited to 'include/tak.c')
| -rw-r--r-- | include/tak.c | 670 |
1 files changed, 379 insertions, 291 deletions
diff --git a/include/tak.c b/include/tak.c index 5fd8e9a..a5f2f44 100644 --- a/include/tak.c +++ b/include/tak.c @@ -16,57 +16,55 @@ */ #include "tak.h" +#include <stdlib.h> // =================================================================== -// Globals +// Helpers // =================================================================== -enum WIN_TYPE won; -uint8_t board_size; -data_t celldat[36]; -colour_stack_t colours[36]; -enum COLOUR current_colour; -uint8_t white_count, black_count, ply; +#define NUM_SQUARES(board_size) (board_size * board_size) -// =================================================================== -// Helpers -// =================================================================== +tak_state_p new_tak_state(const uint8_t board_size) { + tak_state_p state = malloc(sizeof(struct tak_state_s)); + reset_state(state, board_size); + return state; +} -#define NUM_SQUARES (board_size * board_size) +void free_tak_state(tak_state_p state) { free(state); } // =================================================================== // General state stuff // =================================================================== -void -reset_state(const uint8_t new_board_size) { +void reset_state(tak_state_p state, const uint8_t new_board_size) { if (new_board_size == 6) { - board_size = 6; - white_count = 128 | 30; - black_count = 128 | 30; + state->board_size = 6; + state->white_count = 128 | 30; + state->black_count = 128 | 30; } else { - board_size = 5; - white_count = 128 | 21; - black_count = 128 | 21; + state->board_size = 5; + state->white_count = 128 | 21; + state->black_count = 128 | 21; } - ply = 0; - won = 0xFF; // i may live to regret this hack - current_colour = C_BLACK; + state->ply = 0; + state->won = 0xFF; // i may live to regret this hack + state->current_colour = C_BLACK; - for (uint8_t k = 0; k < NUM_SQUARES; k++ ) { - celldat[k] = 0; + for (uint8_t k = 0; k < NUM_SQUARES(new_board_size); k++) { + state->celldat[k] = 0; } } -void -next_ply(void) { - ply++; - if (ply == 2) { - current_colour = C_WHITE; +void next_ply(tak_state_p state) { + state->ply++; + if (state->ply == 2) { + state->current_colour = C_WHITE; } else { - if (current_colour == C_BLACK) current_colour = C_WHITE; - else current_colour = C_BLACK; + if (state->current_colour == C_BLACK) + state->current_colour = C_WHITE; + else + state->current_colour = C_BLACK; } } @@ -74,47 +72,57 @@ next_ply(void) { // Placing stones // =================================================================== -enum ACT_RESULT -try_place(const int8_t location, const enum COLOUR colour, - const enum STONE_VARIANT stone) -{ +enum ACT_RESULT try_place(tak_state_p state, const int8_t location, + const enum COLOUR colour, + const enum STONE_VARIANT stone) { // Game is over? - if (won < 0xFF) return GAME_END; + if (state->won < 0xFF) + return GAME_END; // Can't place on an occupied square - if (COUNT_AT(location)) { + if (COUNT_AT(state, location)) { return ACT_ILLEGAL; } else { switch (stone) { - case STONE_STANDING: - if (ply < 2) return ACT_ILLEGAL; - // behold the magic GCC comment which defeates - // -Wimplicit-fallthrough: - // fall through - case STONE_FLAT: { - if (colour == C_BLACK) { - if (black_count & 127) black_count--; - else return ACT_ILLEGAL; - } else { - if (white_count & 127) white_count--; - else return ACT_ILLEGAL; - } - break; + case STONE_STANDING: + if (state->ply < 2) + return ACT_ILLEGAL; + // behold the magic GCC comment which defeates + // -Wimplicit-fallthrough: + // fall through + case STONE_FLAT: { + if (colour == C_BLACK) { + if (state->black_count & 127) + state->black_count--; + else + return ACT_ILLEGAL; + } else { + if (state->white_count & 127) + state->white_count--; + else + return ACT_ILLEGAL; } - case STONE_CAPSTONE: { - if (ply < 2) return ACT_ILLEGAL; - if (colour == C_BLACK) { - if (black_count & 128) black_count &= 127; - else return ACT_ILLEGAL; - } else { - if (white_count & 128) white_count &= 127; - else return ACT_ILLEGAL; - } - break; + break; + } + case STONE_CAPSTONE: { + if (state->ply < 2) + return ACT_ILLEGAL; + if (colour == C_BLACK) { + if (state->black_count & 128) + state->black_count &= 127; + else + return ACT_ILLEGAL; + } else { + if (state->white_count & 128) + state->white_count &= 127; + else + return ACT_ILLEGAL; } + break; + } } - colours[location] = colour; - celldat[location] = NUM_INC | stone; + state->colours[location] = colour; + state->celldat[location] = NUM_INC | stone; return ACT_OK; } } @@ -123,59 +131,63 @@ try_place(const int8_t location, const enum COLOUR colour, // Moving stacks // =================================================================== -static inline void -push_stones(const int8_t location, const uint8_t count, - const uint8_t new_colours, - const enum STONE_VARIANT top_stone) { - colours[location] = (colours[location] << count) | new_colours; - celldat[location] = top_stone - | ((celldat[location] + ((count << NUM_SHIFT))) & NUM_MASK); +static inline void push_stones(tak_state_p state, const int8_t location, + const uint8_t count, const uint8_t new_colours, + const enum STONE_VARIANT top_stone) { + state->colours[location] = (state->colours[location] << count) | new_colours; + state->celldat[location] = + top_stone | + ((state->celldat[location] + ((count << NUM_SHIFT))) & NUM_MASK); } -enum ACT_RESULT -try_move(const int8_t location, const enum MOVE_DIRECTION direction, - const uint8_t steps, const uint8_t drops[5]) { +enum ACT_RESULT try_move(tak_state_p state, const int8_t location, + const enum MOVE_DIRECTION direction, + const uint8_t steps, const uint8_t drops[5]) { // Game is over? - if (won < 0xFF) return GAME_END; + if (state->won < 0xFF) + return GAME_END; // Can't do this - if (steps == 0 || steps > board_size) return ACT_ILLEGAL; + if (steps == 0 || steps > state->board_size) + return ACT_ILLEGAL; // Check for stones at all - const uint8_t avail = COUNT_AT(location); - if (avail == 0) return ACT_ILLEGAL; + const uint8_t avail = COUNT_AT(state, location); + if (avail == 0) + return ACT_ILLEGAL; // Does the current player own the pile? - if ((colours[location] & 1) != current_colour) return ACT_ILLEGAL; + if ((state->colours[location] & 1) != state->current_colour) + return ACT_ILLEGAL; // Is the desired direction and count on the board? int8_t delta = 0; switch (direction) { - case M_UP: { - delta = +board_size; - if (location + delta * steps > NUM_SQUARES) - return ACT_ILLEGAL; - break; - }; - case M_DOWN: { - delta = -board_size; - if (location + delta * steps < 0) - return ACT_ILLEGAL; - break; - }; - case M_RIGHT: { - delta = +1; - if ((location + steps * delta) / board_size - > location / board_size) - return ACT_ILLEGAL; - break; - }; - case M_LEFT: { - delta = -1; - // We need the extra check for zero here because, irritatingly, - // -1 / board_size == 1 / board_size - if ((location + steps * delta < 0) || - ((location + steps * delta) / board_size - < location / board_size)) - return ACT_ILLEGAL; - break; - }; + case M_UP: { + delta = +state->board_size; + if (location + delta * steps > NUM_SQUARES(state->board_size)) + return ACT_ILLEGAL; + break; + }; + case M_DOWN: { + delta = -state->board_size; + if (location + delta * steps < 0) + return ACT_ILLEGAL; + break; + }; + case M_RIGHT: { + delta = +1; + if ((location + steps * delta) / state->board_size > + location / state->board_size) + return ACT_ILLEGAL; + break; + }; + case M_LEFT: { + delta = -1; + // We need the extra check for zero here because, irritatingly, + // -1 / board_size == 1 / board_size + if ((location + steps * delta < 0) || + ((location + steps * delta) / state->board_size < + location / state->board_size)) + return ACT_ILLEGAL; + break; + }; }; // For every square in the direction @@ -185,27 +197,25 @@ try_move(const int8_t location, const enum MOVE_DIRECTION direction, if (drops[k] == 0) return ACT_ILLEGAL; // Can't drop more than BOARD_SIZE stones in a square - if (drops[k] > board_size) + if (drops[k] > state->board_size) return ACT_ILLEGAL; // Check for overflows - if (COUNT_AT(location+(k+1)*delta) + drops[k] > 0x0F) + if (COUNT_AT(state, location + (k + 1) * delta) + drops[k] > 0x0F) return ACT_OVERFLOW; // Check for capstone - if (STONE_AT(location+(k+1)*delta) == STONE_CAPSTONE) + if (STONE_AT(state, location + (k + 1) * delta) == STONE_CAPSTONE) return ACT_ILLEGAL; // Check for wall - if ( (STONE_AT(location+(k+1)*delta) == STONE_STANDING) - // If not last drop, or not dropping just one, or not a cap - && ( (k+1 < steps) - || (drops[k] != 1) - || (STONE_AT(location) != STONE_CAPSTONE) ) - ) + if ((STONE_AT(state, location + (k + 1) * delta) == STONE_STANDING) + // If not last drop, or not dropping just one, or not a cap + && ((k + 1 < steps) || (drops[k] != 1) || + (STONE_AT(state, location) != STONE_CAPSTONE))) return ACT_ILLEGAL; total += drops[k]; } // Can't ask to move 0, more than board_size, or stones available - if ( (total == 0) || (total > board_size) || (total > avail) ) + if ((total == 0) || (total > state->board_size) || (total > avail)) return ACT_ILLEGAL; // Nothing illegal, do it. First we add the stones to the @@ -213,15 +223,14 @@ try_move(const int8_t location, const enum MOVE_DIRECTION direction, uint8_t j = total; for (uint8_t k = 0; k < steps; k++) { j -= drops[k]; - push_stones(location+(k+1)*delta, - drops[k], - (colours[location] >> j) & (0xFFFF >> (0x10 - drops[k])), - (k == steps - 1) ? STONE_AT(location) : STONE_FLAT); + push_stones(state, location + (k + 1) * delta, drops[k], + (state->colours[location] >> j) & (0xFFFF >> (0x10 - drops[k])), + (k == steps - 1) ? STONE_AT(state, location) : STONE_FLAT); } // Then we drop them from the source - colours[location] >>= total; - const uint8_t dec_count = celldat[location] - (total << NUM_SHIFT); - celldat[location] = dec_count & NUM_MASK; + state->colours[location] >>= total; + const uint8_t dec_count = state->celldat[location] - (total << NUM_SHIFT); + state->celldat[location] = dec_count & NUM_MASK; return ACT_OK; } @@ -231,9 +240,10 @@ try_move(const int8_t location, const enum MOVE_DIRECTION direction, // =================================================================== // Check for the presence of a road connecting opposite sides -static enum WIN_TYPE -check_road_colour(const enum COLOUR colour) { - int component[NUM_SQUARES], touching[NUM_SQUARES]; +static enum WIN_TYPE check_road_colour(tak_state_p state, + const enum COLOUR colour) { + int component[NUM_SQUARES(state->board_size)], + touching[NUM_SQUARES(state->board_size)]; /* We're doing a poor version of a disjoint set data structure to @@ -249,10 +259,10 @@ check_road_colour(const enum COLOUR colour) { the same reason we also don't do path flattening/halving or anything. */ - for (int k=0; k<NUM_SQUARES; k++) { + for (int k = 0; k < NUM_SQUARES(state->board_size); k++) { component[k] = k; // every square is in its own connected // component initially - touching[k] = 0; // and not connected to any sides + touching[k] = 0; // and not connected to any sides } // touching is the bit mask for connectivity, @@ -260,90 +270,95 @@ check_road_colour(const enum COLOUR colour) { // 1 2 4 8 int touch = 5; - for (int row = 0; row < board_size; row++) { - for (int col = 0; col < board_size; col++) { - const int cur = THE_COORDS(col, row); - if (COUNT_AT(cur) - && (colours[cur] & 1) == colour - && STONE_AT(cur) != STONE_STANDING) { - - // do we have any neighbours to the left and below? - const int left_neighbour = ((cur % board_size > 0) - && (COUNT_AT(cur - 1)) // wont ever be out of bounds - && ((colours[cur - 1] & 1) == colour) - && (STONE_AT(cur - 1) != STONE_STANDING)); - - const int lowr_neighbour = ((cur >= board_size) - && (COUNT_AT(cur - board_size)) - && ((colours[cur - board_size] & 1) == colour) - && (STONE_AT(cur - board_size) != STONE_STANDING)); - - // always take the component of the lower neighbour if - // possible, failing that take the left neighbour, otherwise - // we're not yet connected, so update our own component. - if (lowr_neighbour) { - // look up the root of the lower neighbour - int root = cur - board_size; - while (root != component[root]) - root = component[root]; - // join the set - component[cur] = root; - if (touch) { - // something new - touching[root] |= touch; - // are we done? - if ( (touching[root] & 0x3) == 0x3 || (touching[root] & 0xC) == 0xC) - return (colour == C_BLACK) ? WIN_ROAD_BLACK : WIN_ROAD_WHITE; - } - // if we also have a left neighbour then we should `merge' - // sets, and here we assume that the left neighbour set is - // always smaller (may not be) for the direction of merge - if (left_neighbour) { - int left_root = cur - 1; - while (left_root != component[left_root]) - left_root = component[left_root]; - // merge - const int left_touch = touching[left_root]; - if (left_touch) { - touching[root] |= left_touch; - if ( (touching[root] & 0x3) == 0x3 || (touching[root] & 0xC) == 0xC) - return (colour == C_BLACK) ? WIN_ROAD_BLACK : WIN_ROAD_WHITE; - } - component[left_root] = root; - } - } else if (left_neighbour) { - int root = cur - 1; - while (root != component[root]) - root = component[root]; - component[cur] = root; - if (touch) { - touching[root] |= touch; - if ( (touching[root] & 0x3) == 0x3 || (touching[root] & 0xC) == 0xC) - return (colour == C_BLACK) ? WIN_ROAD_BLACK : WIN_ROAD_WHITE; - } - } else if (touch) { - // we had no left or lower neighbour, so we're on our own - touching[cur] = touch; - } + for (int row = 0; row < state->board_size; row++) { + for (int col = 0; col < state->board_size; col++) { + const int cur = THE_COORDS(state->board_size, col, row); + if (COUNT_AT(state, cur) && (state->colours[cur] & 1) == colour && + STONE_AT(state, cur) != STONE_STANDING) { + + // do we have any neighbours to the left and below? + const int left_neighbour = + ((cur % state->board_size > 0) && + (COUNT_AT(state, cur - 1)) // wont ever be out of bounds + && ((state->colours[cur - 1] & 1) == colour) && + (STONE_AT(state, cur - 1) != STONE_STANDING)); + + const int lowr_neighbour = + ((cur >= state->board_size) && + (COUNT_AT(state, cur - state->board_size)) && + ((state->colours[cur - state->board_size] & 1) == colour) && + (STONE_AT(state, cur - state->board_size) != STONE_STANDING)); + + // always take the component of the lower neighbour if + // possible, failing that take the left neighbour, otherwise + // we're not yet connected, so update our own component. + if (lowr_neighbour) { + // look up the root of the lower neighbour + int root = cur - state->board_size; + while (root != component[root]) + root = component[root]; + // join the set + component[cur] = root; + if (touch) { + // something new + touching[root] |= touch; + // are we done? + if ((touching[root] & 0x3) == 0x3 || (touching[root] & 0xC) == 0xC) + return (colour == C_BLACK) ? WIN_ROAD_BLACK : WIN_ROAD_WHITE; + } + // if we also have a left neighbour then we should `merge' + // sets, and here we assume that the left neighbour set is + // always smaller (may not be) for the direction of merge + if (left_neighbour) { + int left_root = cur - 1; + while (left_root != component[left_root]) + left_root = component[left_root]; + // merge + const int left_touch = touching[left_root]; + if (left_touch) { + touching[root] |= left_touch; + if ((touching[root] & 0x3) == 0x3 || + (touching[root] & 0xC) == 0xC) + return (colour == C_BLACK) ? WIN_ROAD_BLACK : WIN_ROAD_WHITE; + } + component[left_root] = root; + } + } else if (left_neighbour) { + int root = cur - 1; + while (root != component[root]) + root = component[root]; + component[cur] = root; + if (touch) { + touching[root] |= touch; + if ((touching[root] & 0x3) == 0x3 || (touching[root] & 0xC) == 0xC) + return (colour == C_BLACK) ? WIN_ROAD_BLACK : WIN_ROAD_WHITE; + } + } else if (touch) { + // we had no left or lower neighbour, so we're on our own + touching[cur] = touch; + } } - if (col + 2 == board_size) touch |= 8; - else touch &= 0x3; + if (col + 2 == state->board_size) + touch |= 8; + else + touch &= 0x3; } - if (row + 2 == board_size) touch = 6; - else touch = 4; + if (row + 2 == state->board_size) + touch = 6; + else + touch = 4; } return 0xFF; } -enum WIN_TYPE -check_win(void) { +enum WIN_TYPE check_win(tak_state_p state) { // Road? enum WIN_TYPE rb, rw; - rb = check_road_colour(C_BLACK); - rw = check_road_colour(C_WHITE); + rb = check_road_colour(state, C_BLACK); + rw = check_road_colour(state, C_WHITE); if (rb == WIN_ROAD_BLACK && rw == WIN_ROAD_WHITE) { - return (ply & 1) ? rb : rw; // Dragons + return (state->ply & 1) ? rb : rw; // Dragons } else if (rw == WIN_ROAD_WHITE) { return rw; } else if (rb == WIN_ROAD_BLACK) { @@ -352,18 +367,21 @@ check_win(void) { // Do we do a flat count? int8_t total = 0, board_full = 1; - for (uint8_t k = 0; k < NUM_SQUARES; k++) { - if (COUNT_AT(k) == 0) { + for (uint8_t k = 0; k < NUM_SQUARES(state->board_size); k++) { + if (COUNT_AT(state, k) == 0) { board_full = 0; - } else if (STONE_AT(k) == STONE_FLAT) { - total += ((colours[k] & 1) == C_BLACK) ? +1 : -1 ; + } else if (STONE_AT(state, k) == STONE_FLAT) { + total += ((state->colours[k] & 1) == C_BLACK) ? +1 : -1; } } - if (black_count == 0 || white_count == 0 || board_full) { + if (state->black_count == 0 || state->white_count == 0 || board_full) { // Decide based on count - if (total > 0) return WIN_FLAT_BLACK; - else if (total < 0) return WIN_FLAT_WHITE; - else return WIN_DRAW; + if (total > 0) + return WIN_FLAT_BLACK; + else if (total < 0) + return WIN_FLAT_WHITE; + else + return WIN_DRAW; } return 0xFF; @@ -372,38 +390,57 @@ check_win(void) { // PTN place parser // =================================================================== -#define NULL 0 - -#define ASSERT_NONEMPTY { \ - if (ptn == NULL || *ptn == 0) return PTN_INVALID; \ +#define ASSERT_NONEMPTY \ + { \ + if (ptn == NULL || *ptn == 0) \ + return PTN_INVALID; \ } -#define ASSERT_MORE { if (*ptn == 0) return PTN_INVALID; } +#define ASSERT_MORE \ + { \ + if (*ptn == 0) \ + return PTN_INVALID; \ + } -enum PTN_RESULT -parse_place(char *ptn, uint8_t *out_location, - enum STONE_VARIANT *out_stone) { +enum PTN_RESULT parse_place(const uint8_t board_size, char *ptn, + uint8_t *out_location, + enum STONE_VARIANT *out_stone) { ASSERT_NONEMPTY; *out_stone = STONE_FLAT; switch (*ptn) { - case 'C' : { ptn++; *out_stone = STONE_CAPSTONE; break; }; - case 'S' : { ptn++; *out_stone = STONE_STANDING; break; }; - case 'F' : { ptn++; break; }; + case 'C': { + ptn++; + *out_stone = STONE_CAPSTONE; + break; + }; + case 'S': { + ptn++; + *out_stone = STONE_STANDING; + break; + }; + case 'F': { + ptn++; + break; + }; } ASSERT_MORE; - if ( (*ptn < 'a') || (*ptn > '`' + board_size) ) return PTN_INVALID; + if ((*ptn < 'a') || (*ptn > '`' + board_size)) + return PTN_INVALID; *out_location = *ptn - 'a'; - ptn++; ASSERT_MORE; + ptn++; + ASSERT_MORE; - if ( (*ptn < '1') || (*ptn > board_size + '0') ) return PTN_INVALID; + if ((*ptn < '1') || (*ptn > board_size + '0')) + return PTN_INVALID; *out_location += board_size * (*ptn - '1'); - if (*(++ptn) > 0) return PTN_INVALID; + if (*(++ptn) > 0) + return PTN_INVALID; return PTN_OK; } @@ -412,40 +449,58 @@ parse_place(char *ptn, uint8_t *out_location, // PTN move parser // =================================================================== -enum PTN_RESULT -parse_move(char *ptn, uint8_t *out_location, - enum MOVE_DIRECTION *out_direction, - uint8_t *out_steps, uint8_t out_drops[5]) { +enum PTN_RESULT parse_move(const uint8_t board_size, char *ptn, + uint8_t *out_location, + enum MOVE_DIRECTION *out_direction, + uint8_t *out_steps, uint8_t out_drops[5]) { ASSERT_NONEMPTY; uint8_t picked_up = 1; // Optionally indicate how many stones picked up - if ( (*ptn >= '1') && (*ptn <= '0' + board_size)) { + if ((*ptn >= '1') && (*ptn <= '0' + board_size)) { picked_up = *ptn - '0'; - ptn++; ASSERT_MORE; + ptn++; + ASSERT_MORE; } // column must be on the board - if ( (*ptn < 'a') || (*ptn > '`' + board_size) ) return PTN_INVALID; + if ((*ptn < 'a') || (*ptn > '`' + board_size)) + return PTN_INVALID; *out_location = *ptn - 'a'; - ptn++; ASSERT_MORE; + ptn++; + ASSERT_MORE; // row must be on the board - if ( (*ptn < '1') || (*ptn > board_size + '0') ) return PTN_INVALID; + if ((*ptn < '1') || (*ptn > board_size + '0')) + return PTN_INVALID; *out_location += board_size * (*ptn - '1'); - ptn++; ASSERT_MORE; + ptn++; + ASSERT_MORE; // valid direction switch (*ptn) { - case '+': { *out_direction = M_UP; break; } - case '-': { *out_direction = M_DOWN; break; } - case '<': { *out_direction = M_LEFT; break; } - case '>': { *out_direction = M_RIGHT; break; } - default: return PTN_INVALID; + case '+': { + *out_direction = M_UP; + break; + } + case '-': { + *out_direction = M_DOWN; + break; + } + case '<': { + *out_direction = M_LEFT; + break; + } + case '>': { + *out_direction = M_RIGHT; + break; + } + default: + return PTN_INVALID; } // Handle the case 'n<column><row><direction>' as @@ -463,11 +518,11 @@ parse_move(char *ptn, uint8_t *out_location, uint8_t total = 0; while (*ptn) { // can't drop more than the carry limit, or less than 1 - if ( (*ptn < '1') || (*ptn > '0' + board_size) ) + if ((*ptn < '1') || (*ptn > '0' + board_size)) return PTN_INVALID; // can't move more than the size of the board in any direction - if ( (*out_steps + 1 >= board_size) && *ptn) + if ((*out_steps + 1 >= board_size) && *ptn) return PTN_INVALID; out_drops[*out_steps] = *ptn - '0'; @@ -477,7 +532,8 @@ parse_move(char *ptn, uint8_t *out_location, } // Mismatch between number of stones picked up and total dropped - if ( total != picked_up ) return PTN_INVALID; + if (total != picked_up) + return PTN_INVALID; return PTN_OK; } @@ -486,16 +542,27 @@ parse_move(char *ptn, uint8_t *out_location, // Generate PTN for place // =================================================================== -void -generate_place(const uint8_t in_location, - const enum STONE_VARIANT in_stone, char out_ptn[4]) { +void generate_place(const uint8_t board_size, const uint8_t in_location, + const enum STONE_VARIANT in_stone, char out_ptn[4]) { switch (in_stone) { - case STONE_FLAT: { break; } - case STONE_STANDING: { *out_ptn = 'S'; out_ptn++; break; } - case STONE_CAPSTONE: { *out_ptn = 'C'; out_ptn++; break; } + case STONE_FLAT: { + break; + } + case STONE_STANDING: { + *out_ptn = 'S'; + out_ptn++; + break; + } + case STONE_CAPSTONE: { + *out_ptn = 'C'; + out_ptn++; + break; } - *out_ptn = 'a' + (in_location % board_size); out_ptn++; - *out_ptn = '1' + (in_location / board_size); out_ptn++; + } + *out_ptn = 'a' + (in_location % board_size); + out_ptn++; + *out_ptn = '1' + (in_location / board_size); + out_ptn++; *out_ptn = 0; } @@ -503,29 +570,46 @@ generate_place(const uint8_t in_location, // Generate PTN for move // =================================================================== -void -generate_move(const uint8_t in_location, - const enum MOVE_DIRECTION in_direction, - const uint8_t in_steps, const uint8_t in_drops[5], - char out_ptn[10]) { +void generate_move(const uint8_t board_size, const uint8_t in_location, + const enum MOVE_DIRECTION in_direction, + const uint8_t in_steps, const uint8_t in_drops[5], + char out_ptn[10]) { uint8_t total = 0; - for (uint8_t k = 0; k<in_steps; k++) total+=in_drops[k]; + for (uint8_t k = 0; k < in_steps; k++) + total += in_drops[k]; if (total > 1) { - *out_ptn = '0' + total; out_ptn++; + *out_ptn = '0' + total; + out_ptn++; } - *out_ptn = 'a' + (in_location % board_size); out_ptn++; - *out_ptn = '1' + (in_location / board_size); out_ptn++; + *out_ptn = 'a' + (in_location % board_size); + out_ptn++; + *out_ptn = '1' + (in_location / board_size); + out_ptn++; switch (in_direction) { - case M_UP: { *out_ptn = '+'; break; } - case M_DOWN: { *out_ptn = '-'; break; } - case M_LEFT: { *out_ptn = '<'; break; } - case M_RIGHT: { *out_ptn = '>'; break; } - }; out_ptn++; + case M_UP: { + *out_ptn = '+'; + break; + } + case M_DOWN: { + *out_ptn = '-'; + break; + } + case M_LEFT: { + *out_ptn = '<'; + break; + } + case M_RIGHT: { + *out_ptn = '>'; + break; + } + }; + out_ptn++; for (uint8_t k = 0; (total > 1) && (k < in_steps); k++) { - *out_ptn = '0' + in_drops[k]; out_ptn++; + *out_ptn = '0' + in_drops[k]; + out_ptn++; } *out_ptn = 0; @@ -535,24 +619,26 @@ generate_move(const uint8_t in_location, // Driver // =================================================================== -static uint8_t -is_not_placement(char *ptn) { - if (ptn == 0) return 0; - for (;;ptn++) { +static uint8_t is_not_placement(char *ptn) { + if (ptn == 0) + return 0; + for (;; ptn++) { switch (*ptn) { - case '+': - case '-': - case '>': - case '<': return 1; - case 0: return 0; + case '+': + case '-': + case '>': + case '<': + return 1; + case 0: + return 0; } } } -enum ACT_RESULT -do_ptn(char *ptn) { +enum ACT_RESULT do_ptn(tak_state_p state, char *ptn) { // Game over? - if (won < 0xFF) return GAME_END; + if (state->won < 0xFF) + return GAME_END; enum PTN_RESULT ptn_res; enum ACT_RESULT act_res; @@ -563,11 +649,13 @@ do_ptn(char *ptn) { uint8_t steps, drops[5]; enum MOVE_DIRECTION direction; // Parse it as a move - ptn_res = parse_move(ptn, &location, &direction, &steps, drops); + ptn_res = parse_move(state->board_size, ptn, &location, &direction, &steps, + drops); // If valid PTN, try to do it if (ptn_res == PTN_OK) { - if (ply < 2) return ACT_ILLEGAL; - act_res = try_move(location, direction, steps, drops); + if (state->ply < 2) + return ACT_ILLEGAL; + act_res = try_move(state, location, direction, steps, drops); } else { return ACT_INVALID_PTN; } @@ -575,10 +663,10 @@ do_ptn(char *ptn) { // It was not a move enum STONE_VARIANT stone; // Was it a valid placement? - ptn_res = parse_place(ptn, &location, &stone); + ptn_res = parse_place(state->board_size, ptn, &location, &stone); // If so, try it if (ptn_res == PTN_OK) - act_res = try_place(location, current_colour, stone); + act_res = try_place(state, location, state->current_colour, stone); else return ACT_INVALID_PTN; } @@ -586,16 +674,16 @@ do_ptn(char *ptn) { if (act_res == ACT_OK) { // Don't bother checking that the game was won early on, could be // more conservative here :) - if (ply >= board_size) { - won = check_win(); - if (won < 0xFF) { - // Winning move, but no need to update current colour - ply++; - return GAME_END; + if (state->ply >= state->board_size) { + state->won = check_win(state); + if (state->won < 0xFF) { + // Winning move, but no need to update current colour + state->ply++; + return GAME_END; } } // Only step if the game isn't over yet - next_ply(); + next_ply(state); } return act_res; } |
