From 40c6b1dafab4de169bac8a799e7953e85061218e Mon Sep 17 00:00:00 2001 From: tslil clingman Date: Tue, 2 Feb 2021 19:23:54 -0500 Subject: TEI interface working! --- include/tak.c | 61 +++++++++-------- include/tak.h | 26 ++++--- include/tps.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/tps.h | 11 +++ 4 files changed, 275 insertions(+), 40 deletions(-) create mode 100644 include/tps.c create mode 100644 include/tps.h (limited to 'include') 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>=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 + +#include // for strnlen, strncmp, and strcpy +#include // 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); -- cgit v1.2.3