aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/tak.c61
-rw-r--r--include/tak.h26
-rw-r--r--include/tps.c217
-rw-r--r--include/tps.h11
4 files changed, 275 insertions, 40 deletions
diff --git a/include/tak.c b/include/tak.c
index c9c761c..783bea8 100644
--- a/include/tak.c
+++ b/include/tak.c
@@ -77,7 +77,7 @@ next_ply(void) {
// Placing stones
// ===================================================================
-enum E_RESULT
+enum ACT_RESULT
try_place(const int8_t location, const enum COLOUR colour,
const enum STONE_VARIANT stone)
{
@@ -135,7 +135,7 @@ push_stones(const int8_t location, const uint8_t count,
| ((celldat[location] + ((count << NUM_SHIFT))) & NUM_MASK);
}
-enum E_RESULT
+enum ACT_RESULT
try_move(const int8_t location, const enum MOVE_DIRECTION direction,
const uint8_t steps, const uint8_t drops[5]) {
// Game is over?
@@ -380,14 +380,17 @@ check_win(void) {
// PTN place parser
// ===================================================================
-#define ASSERT_NONEMPTY { if (ptn == 0 || *ptn == 0) return PTN_INVALID; }
-#define ASSERT_MORE { if (*ptn == 0) return PTN_INVALID; }
+#define NULL 0
-enum E_RESULT
-parse_place(const uint8_t board_size, char *ptn,
- uint8_t *out_location, enum STONE_VARIANT *out_stone) {
+#define ASSERT_NONEMPTY { \
+ if (ptn == NULL || *ptn == 0) return PTN_INVALID; \
+ }
+
+#define ASSERT_MORE { if (*ptn == 0) return PTN_INVALID; }
- if (board_size < 5 || board_size > 6) return PTN_INVALID;
+enum PTN_RESULT
+parse_place(char *ptn, uint8_t *out_location,
+ enum STONE_VARIANT *out_stone) {
ASSERT_NONEMPTY;
@@ -410,20 +413,18 @@ parse_place(const uint8_t board_size, char *ptn,
if (*(++ptn) > 0) return PTN_INVALID;
- return PTN_VALID;
+ return PTN_OK;
}
// ===================================================================
// PTN move parser
// ===================================================================
-enum E_RESULT
-parse_move(const uint8_t board_size, char *ptn,
- uint8_t *out_location, enum MOVE_DIRECTION *out_direction,
+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]) {
- if (board_size < 5 || board_size > 6) return PTN_INVALID;
-
ASSERT_NONEMPTY;
uint8_t picked_up = 1;
@@ -462,7 +463,7 @@ parse_move(const uint8_t board_size, char *ptn,
if (*ptn == 0) {
*out_steps = 1;
out_drops[0] = picked_up;
- return PTN_VALID;
+ return PTN_OK;
}
// Parse the drops in each subsequent square
@@ -486,7 +487,7 @@ parse_move(const uint8_t board_size, char *ptn,
// Mismatch between number of stones picked up and total dropped
if ( total != picked_up ) return PTN_INVALID;
- return PTN_VALID;
+ return PTN_OK;
}
// ===================================================================
@@ -531,7 +532,6 @@ generate_move(const uint8_t in_location,
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++;
}
@@ -557,12 +557,13 @@ is_not_placement(char *ptn) {
}
}
-enum E_RESULT
+enum ACT_RESULT
do_ptn(char *ptn) {
// Game over?
if (won < 0xFF) return GAME_END;
- enum E_RESULT res;
+ enum PTN_RESULT ptn_res;
+ enum ACT_RESULT act_res;
uint8_t location;
// Placing or moving?
@@ -570,24 +571,27 @@ do_ptn(char *ptn) {
uint8_t steps, drops[5];
enum MOVE_DIRECTION direction;
// Parse it as a move
- res = parse_move(board_size, ptn, &location,
- &direction, &steps, drops);
+ ptn_res = parse_move(ptn, &location, &direction, &steps, drops);
// If valid PTN, try to do it
- if (res == PTN_VALID) {
+ if (ptn_res == PTN_OK) {
if (ply < 2) return ACT_ILLEGAL;
- res = try_move(location, direction, steps, drops);
+ act_res = try_move(location, direction, steps, drops);
+ } else {
+ return ACT_INVALID_PTN;
}
} else {
// It was not a move
enum STONE_VARIANT stone;
// Was it a valid placement?
- res = parse_place(board_size, ptn, &location, &stone);
+ ptn_res = parse_place(ptn, &location, &stone);
// If so, try it
- if (res == PTN_VALID)
- res = try_place(location, current_colour, stone);
+ if (ptn_res == PTN_OK)
+ act_res = try_place(location, current_colour, stone);
+ else
+ return ACT_INVALID_PTN;
}
// A valid ply occured
- if (res == ACT_OK) {
+ 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) {
@@ -601,6 +605,5 @@ do_ptn(char *ptn) {
// Only step if the game isn't over yet
next_ply();
}
-
- return res;
+ return act_res;
}
diff --git a/include/tak.h b/include/tak.h
index 8b40430..63dffe1 100644
--- a/include/tak.h
+++ b/include/tak.h
@@ -24,8 +24,10 @@
// Types
// ===================================================================
-enum E_RESULT {ACT_OK, ACT_ILLEGAL, ACT_OVERFLOW,
- PTN_VALID, PTN_INVALID, GAME_END };
+enum ACT_RESULT { ACT_OK, ACT_ILLEGAL, ACT_OVERFLOW, ACT_INVALID_PTN, GAME_END };
+
+enum PTN_RESULT { PTN_OK, PTN_INVALID };
+enum TPS_RESULT { TPS_OK, TPS_INVALID };
enum COLOUR { C_WHITE, C_BLACK };
enum STONE_VARIANT { STONE_FLAT, STONE_STANDING, STONE_CAPSTONE };
@@ -49,6 +51,8 @@ typedef uint16_t colour_stack_t;
// Variables
// ===================================================================
+// NOTE: We only support one capstone per player and 5s or 6s games.
+
extern enum WIN_TYPE won;
extern uint8_t board_size;
extern data_t celldat[36];
@@ -66,11 +70,11 @@ extern uint8_t white_count, black_count, ply;
void reset_state(const uint8_t new_board_size);
void next_ply(void);
-enum E_RESULT
+enum ACT_RESULT
try_place(const int8_t location, const enum COLOUR colour,
const enum STONE_VARIANT stone);
-enum E_RESULT
+enum ACT_RESULT
try_move(const int8_t location, const enum MOVE_DIRECTION direction,
const uint8_t steps, const uint8_t drops[5]);
@@ -80,13 +84,13 @@ check_win(void);
// -------------------------------------------------------------------
// PTN related
-enum E_RESULT
-parse_place(const uint8_t board_size, char *ptn,
- uint8_t *out_location, enum STONE_VARIANT *out_stone);
+enum PTN_RESULT
+parse_place(char *in_ptn, uint8_t *out_location,
+ enum STONE_VARIANT *out_stone);
-enum E_RESULT
-parse_move(const uint8_t board_size, char *ptn,
- uint8_t *out_location, enum MOVE_DIRECTION *out_direction,
+enum PTN_RESULT
+parse_move(char *in_ptn, uint8_t *out_location,
+ enum MOVE_DIRECTION *out_direction,
uint8_t *out_steps, uint8_t out_drops[5]);
void
@@ -102,6 +106,6 @@ generate_move(const uint8_t in_location,
// -------------------------------------------------------------------
// Game driver
-enum E_RESULT
+enum ACT_RESULT
do_ptn(char *ptn);
#endif
diff --git a/include/tps.c b/include/tps.c
new file mode 100644
index 0000000..be25c4f
--- /dev/null
+++ b/include/tps.c
@@ -0,0 +1,217 @@
+#include "tps.h"
+
+// ===================================================================
+// Load TPS string
+// ===================================================================
+
+#define TPS_ASSERT_MORE { if (*tps == 0) return TPS_INVALID; }
+
+enum TPS_RESULT
+load_tps(char* tps) {
+ // TODO: Ensure NULL termination?
+ if (tps == NULL) return TPS_INVALID;
+
+ uint8_t prefix = 0;
+ // Check if we're likely of the form [TPS "blah"]
+ if (!strncmp(tps, "[TPS \"", 6)) {
+ prefix=1;
+ // Now we can worry about just the TPS part
+ tps += 6;
+ }
+
+ // Reset everything
+ reset_state(board_size);
+
+ // Parse squares, NOTE: We assume that board_size matches TPS size.
+ int col = 0, row = board_size-1, skip, parsing = 1;
+ while (parsing) {
+ switch (*tps) {
+ case ' ': {
+ // we're done
+ parsing = 0;
+ tps++; TPS_ASSERT_MORE;
+ break;
+ }
+ case 'x': {
+ // empty squares
+ tps++; TPS_ASSERT_MORE;
+ skip = 0;
+ if (*tps >= '2' && *tps <= '0'+board_size) {
+ skip = *tps - '1';
+ tps++; TPS_ASSERT_MORE;
+ } else if (*tps != ',' && *tps != '/' && *tps != ' ') {
+ return TPS_INVALID;
+ }
+ col += skip;
+ if (col >= board_size + 1) return TPS_INVALID;
+ break;
+ }
+ case '/': {
+ // next row
+ if (col + 1 != board_size) return TPS_INVALID;
+ row--; col = 0;
+ if (row < 0) return TPS_INVALID;
+ tps++; TPS_ASSERT_MORE;
+ break;
+ }
+ case ',': {
+ // next column
+ col++;
+ if (col >= board_size) return TPS_INVALID;
+ tps++; TPS_ASSERT_MORE;
+ break;
+ }
+ default: {
+ const int l = THE_COORDS(col, row);
+ uint8_t num_read = 0, reading = 1;
+ // Read in a stack of colours, optionally terminated by an S
+ // or C to change the top stone type
+ while (reading) {
+ switch (*tps) {
+ // Reading a stone colour
+ case '2': {
+ // check next letter to make sure we have the material
+ tps++; TPS_ASSERT_MORE;
+ if (*tps == 'C') {
+ if (black_count & 128) black_count &= 127;
+ else return TPS_INVALID;
+ } else if (black_count & 127) {
+ black_count--;
+ } else return TPS_INVALID;
+ colours[l] <<= 1;
+ celldat[l] += NUM_INC;
+ colours[l] |= 1;
+ num_read++;
+ break;
+ }
+ case '1': {
+ tps++; TPS_ASSERT_MORE;
+ if (*tps == 'C') {
+ if (white_count & 128) white_count &= 127;
+ else return TPS_INVALID;
+ } else if (white_count & 127) {
+ white_count--;
+ } else return TPS_INVALID;
+ colours[l] <<= 1;
+ celldat[l] += NUM_INC;
+ num_read++;
+ break;
+ }
+ case 'S': {
+ // Have we already read a stone type?
+ if (STONE_AT(l) != STONE_FLAT) return TPS_INVALID;
+ celldat[l] |= STONE_STANDING;
+ tps++; TPS_ASSERT_MORE;
+ break;
+ }
+ case 'C': {
+ if (STONE_AT(l) != STONE_FLAT) return TPS_INVALID;
+ celldat[l] |= STONE_CAPSTONE;
+ tps++; TPS_ASSERT_MORE;
+ break;
+ }
+ case ',': // fall-through
+ case '/': {
+ // done here
+ reading=0;
+ break;
+ }
+ default: return TPS_INVALID;
+ }
+ if (num_read > 0xF) return TPS_INVALID;
+ }
+ }
+ }
+ }
+
+ // Now it's time to parse the ply number. First, the active player
+ if (*tps != '1' && *tps != '2') return TPS_INVALID;
+ ply += *tps - '1';
+ tps++; TPS_ASSERT_MORE;
+
+ // Space
+ if (*tps != ' ') return TPS_INVALID;
+ tps++; TPS_ASSERT_MORE;
+
+ // Turn number, atoi doesn't detect errors so let's do it ourselves
+ uint8_t p = 0;
+ do {
+ p *= 10;
+ if (*tps >= '0' && *tps <= '9') {
+ p += *tps - '0';
+ } else return TPS_INVALID;
+ tps++;
+ } while ( (prefix && *tps && *tps != '"') || (!prefix && *tps) );
+ if (p == 0) return TPS_INVALID;
+ ply += 2*(p - 1);
+
+ current_colour = (ply & 1) ? C_BLACK : C_WHITE;
+ if (ply < 2) current_colour = C_BLACK - current_colour;
+
+ if (prefix) {
+ tps++; TPS_ASSERT_MORE;
+ if (*tps != ']' ) return TPS_INVALID;
+
+ tps++;
+ if (*tps != 0) return TPS_INVALID;
+ }
+
+ return TPS_OK;
+}
+
+// ===================================================================
+// Generate TPS string
+// ===================================================================
+
+void
+generate_tps(char *out_tps) {
+ strcpy(out_tps, "[TPS \"");
+ out_tps += 6;
+
+ for (int8_t row = board_size - 1; row >= 0; row--) {
+ for (int8_t col = 0; col < board_size; col++) {
+ const int8_t l = THE_COORDS(col, row);
+ const uint8_t count = COUNT_AT(l);
+ if (count) {
+ colour_stack_t c = colours[l], s = 1<<(count - 1);
+ for (int k=0; k<count; k++, s >>=1, out_tps++) {
+ if (c & s) *out_tps = '2';
+ else *out_tps = '1';
+ }
+ switch (STONE_AT(l)) {
+ case STONE_CAPSTONE: {
+ *out_tps = 'C'; out_tps++; break;
+ }
+ case STONE_STANDING: {
+ *out_tps = 'S'; out_tps++; break;
+ }
+ default: break;
+ }
+ } else {
+ int8_t skip = 1;
+ while (col < board_size && COUNT_AT(l+skip) == 0) {
+ skip++;
+ col++;
+ }
+ *out_tps = 'x'; out_tps++;
+ if (skip > 1) {
+ *out_tps = '0'+skip; out_tps++;
+ }
+ }
+ if (col + 1 < board_size) {
+ *out_tps = ','; out_tps++;
+ }
+ }
+ if (row > 0) {
+ *out_tps = '/'; out_tps++;
+ }
+ }
+
+ *out_tps = ' '; out_tps++;
+ *out_tps = '1' + (ply & 1); out_tps++;
+ *out_tps = ' '; out_tps++;
+
+ out_tps += sprintf(out_tps, "%d", ply/2 + 1);
+
+ strcpy(out_tps, "\"]");
+}
diff --git a/include/tps.h b/include/tps.h
new file mode 100644
index 0000000..70cf1de
--- /dev/null
+++ b/include/tps.h
@@ -0,0 +1,11 @@
+#include <tak.h>
+
+#include <string.h> // for strnlen, strncmp, and strcpy
+#include <stdio.h> // for sprintf (in a single place! grrr)
+
+// NOTE: We assume that board_size matches TPS size.
+enum TPS_RESULT
+load_tps(char* in_tps);
+
+void
+generate_tps(char *out_tps);