#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, "\"]"); }