aboutsummaryrefslogtreecommitdiff
path: root/include/tps.c
diff options
context:
space:
mode:
authortslil clingman <tslil@posteo.de>2021-02-02 19:23:54 -0500
committertslil <tslil@posteo.de>2026-08-28 19:37:41 +0100
commit40c6b1dafab4de169bac8a799e7953e85061218e (patch)
tree99d86e8f90e8aa19b35875ea8ba624c50c467857 /include/tps.c
parent11956b2e940f5e1839efab187d898092e819a766 (diff)
TEI interface working!
Diffstat (limited to 'include/tps.c')
-rw-r--r--include/tps.c217
1 files changed, 217 insertions, 0 deletions
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, "\"]");
+}