diff options
| author | tslil clingman <tslil@posteo.de> | 2021-02-02 19:23:54 -0500 |
|---|---|---|
| committer | tslil <tslil@posteo.de> | 2026-08-28 19:37:41 +0100 |
| commit | 40c6b1dafab4de169bac8a799e7953e85061218e (patch) | |
| tree | 99d86e8f90e8aa19b35875ea8ba624c50c467857 /include/tps.c | |
| parent | 11956b2e940f5e1839efab187d898092e819a766 (diff) | |
TEI interface working!
Diffstat (limited to 'include/tps.c')
| -rw-r--r-- | include/tps.c | 217 |
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, "\"]"); +} |
