aboutsummaryrefslogtreecommitdiff
path: root/include/tak.c
diff options
context:
space:
mode:
Diffstat (limited to 'include/tak.c')
-rw-r--r--include/tak.c670
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;
}