From 0223a9bec5535fced1a7698b55fd42155d9b0446 Mon Sep 17 00:00:00 2001 From: tslil clingman Date: Sun, 15 Jan 2023 21:31:00 +0100 Subject: switch to explicit game state & important bug fix & clang format Previously the code base assumed that there was a single, global game state which was the implicit target of all actions taken. Looking ahead at architectural improvements, this has now been (almost entirely) made explicit and functions take tak_state_p where necessary (and also where unnecessary). Two important fixes to actions.c were made: - Previously when generating the possible stack moves, stack height overflows (> 15) were not taken into account and this resulted in the tree search corrupting the board state. Now action search does not list all legal actions, rather the subset of these encodeable by the implementation. - The check for crushing on a stack move was incorrect (too strict), and this resulted in many legitimate moves being igonored. Finally, in other changes, weights have also been improved by training all games instead of some subset for chosen players, and clang-format was run on the codebase. --- include/tak.c | 670 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 379 insertions(+), 291 deletions(-) (limited to 'include/tak.c') 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 // =================================================================== -// 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; kboard_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' 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 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; } -- cgit v1.3.1