diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Makefile | 10 | ||||
| -rw-r--r-- | include/tak.c | 61 | ||||
| -rw-r--r-- | include/tak.h | 26 | ||||
| -rw-r--r-- | include/tps.c | 217 | ||||
| -rw-r--r-- | include/tps.h | 11 | ||||
| -rwxr-xr-x | resources/extract.sh | 2 | ||||
| -rw-r--r-- | src/ct1986.c | 14 | ||||
| -rw-r--r-- | src/ctaklm.c | 32 | ||||
| -rw-r--r-- | src/pptdb.c | 6 | ||||
| -rw-r--r-- | src/tei.c | 163 |
11 files changed, 475 insertions, 68 deletions
@@ -3,4 +3,5 @@ data/ ct1986 ctaklm pptdb +tei buildroot* @@ -1,5 +1,3 @@ -STRIP=strip - IDIR=include DEFINES=-DDETERMINISTIC CFLAGS=-O3 -Wall -Wextra -Wpedantic -std=c99 -D_DEFAULT_SOURCE $(DEFINES) -I$(IDIR) @@ -20,16 +18,18 @@ CROSS_STRIP=$(BUILDROOT_DIR)/output/host/bin/arm-linux-strip ctaklm: src/ctaklm.o $(OBJS) $(CC) $(CFLAGS) src/ctaklm.o $(OBJS) -o ctaklm - $(STRIP) ctaklm pptdb: src/pptdb.o $(CTAK_OBJS) $(CC) $(CFLAGS) src/pptdb.o $(CTAK_OBJS) -o pptdb +tei: src/tei.o $(OBJS) + $(CC) $(CFLAGS) src/tei.o $(OBJS) -o tei + clean: - rm -f ctaklm ct1986 pptdb + rm -f ctaklm ct1986 pptdb tei rm -f src/*.o include/*.o -native: clean ctaklm pptdb +native: clean ctaklm pptdb tei $(CROSS_CC): ./resources/do_buildroot.sh $(BUILDROOT_DIR) 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); diff --git a/resources/extract.sh b/resources/extract.sh index e9cd62a..fb2c30d 100755 --- a/resources/extract.sh +++ b/resources/extract.sh @@ -2,7 +2,7 @@ db_file=games_anon.db -chosen_players="AaaarghBot Tiltak_Bot Taktician" +chosen_players="AaaarghBot Tiltak_Bot TakticianBot" query() { query="(size == $1) and (result != '1-0') and (result != '0-1') and (result != '0-0') and (result != '1/2-1/2')" diff --git a/src/ct1986.c b/src/ct1986.c index 2c2d875..aeff13a 100644 --- a/src/ct1986.c +++ b/src/ct1986.c @@ -84,9 +84,17 @@ handle_turn(char *line) { uint8_t new_win = (won == 0xFF); switch (do_ptn(line)) { // Errors - case PTN_INVALID: { lcd_put_line(L_SCROLL, "Invalid PTN."); break; } - case ACT_ILLEGAL: { lcd_put_line(L_SCROLL, "Illegal ply."); break; } - case ACT_OVERFLOW: { lcd_put_line(L_SCROLL, "Overflow."); break; } + case ACT_INVALID_PTN: { + lcd_put_line(L_SCROLL, "Invalid PTN."); + break; + } + case ACT_ILLEGAL: { + lcd_put_line(L_SCROLL, "Illegal ply."); + break; } + case ACT_OVERFLOW: { + lcd_put_line(L_SCROLL, "Overflow."); + break; + } // Game has ended case GAME_END: { // Did it end this turn? diff --git a/src/ctaklm.c b/src/ctaklm.c index d790769..0948fa7 100644 --- a/src/ctaklm.c +++ b/src/ctaklm.c @@ -22,6 +22,7 @@ #include <string.h> #include <tak.h> +#include <tps.h> #include <negamax.h> static const char *blk = "\033[41m", *wht = "\033[44m"; @@ -220,7 +221,7 @@ handle_turn(char *line) { uint8_t new_win = (won == 0xFF); switch (do_ptn(line)) { // Errors - case PTN_INVALID: { puts("Invalid PTN."); return -1; break; } + case ACT_INVALID_PTN: { puts("Invalid PTN."); return -1; break; } case ACT_ILLEGAL: { puts("Illegal action."); return -1; break; } case ACT_OVERFLOW: { puts("Move would cause internal overflow, select another."); @@ -244,7 +245,6 @@ handle_turn(char *line) { break; } // Valid, append to game log - case PTN_VALID: case ACT_OK: { append_to_gamelog(line, 0); if (auto_board) print_board(); @@ -274,7 +274,7 @@ load_ptn(const char* fn) { if (fh == NULL) return EXIT_FAILURE; int space1, space2; - enum E_RESULT r; + enum ACT_RESULT r; ssize_t read; size_t alloc_size; @@ -315,7 +315,6 @@ load_ptn(const char* fn) { if (line) free(line); fclose(fh); - return EXIT_SUCCESS; } @@ -348,16 +347,12 @@ negamax_turn(void) { float minimax = negamax_generate(); putchar('\n'); // Failed to find a move? - if (minimax <= -infty) { - puts("Opponent failed to find a move!"); - return EXIT_FAILURE; - } else { - printf("Result: %s (%.2f, checked %.1e)\n", - negamax_ptn, - minimax*100.0, - num_check); - return handle_turn(negamax_ptn); - } + if (minimax <= -infty) puts("Opponent concedes!"); + printf("Result: %s (%.2f, checked %.1e)\n", + negamax_ptn, + minimax*100.0, + num_check); + return handle_turn(negamax_ptn); } else { return EXIT_FAILURE; } @@ -366,8 +361,9 @@ negamax_turn(void) { static int input_is_not_turn(const char *line) { if (!strcmp(line,"help")) { - puts("Valid commands: auto (board|info), board, depth [0-9], eval, help,\ -info, load, log, new, play (b|w), self-play, square <col><row>, <PTN>."); + puts("Valid commands: auto (board|info), board, depth [0-9], eval,\ + help, info, load <file.ptn>, log, new, play (b|w), self-play, square\ + <col><row>, tps, <PTN>."); } else if (!strcmp(line,"board")) { print_board(); } else if (!strcmp(line,"info")) { @@ -385,6 +381,10 @@ info, load, log, new, play (b|w), self-play, square <col><row>, <PTN>."); puts(gamelog); } else if (!strcmp(line,"new")) { new_game(5); + } else if (!strcmp(line,"tps")) { + char buf[1000]; + generate_tps(buf); + puts(buf); } else if (!strcmp(line,"self-play")) { while (negamax_turn() == 0); } else if (!strncmp(line,"depth",5)) { diff --git a/src/pptdb.c b/src/pptdb.c index e3fd687..bb94987 100644 --- a/src/pptdb.c +++ b/src/pptdb.c @@ -75,10 +75,10 @@ write_input(const int dx, const int dy, const uint8_t swap) { } // Warning: performs _no_ checks on input whatsoever -static enum E_RESULT +static enum ACT_RESULT parse_line(const char *pt, const ssize_t read) { ssize_t idx; - enum E_RESULT r; + enum ACT_RESULT r; int total_plies = 0; for (idx=0;idx<read;idx++) { @@ -159,7 +159,7 @@ This program comes with ABSOLUTELY NO WARRANTY; and is made available under the int main(int argc, char **argv) { (void)(argc); - enum E_RESULT r; + enum ACT_RESULT r; enum WIN_TYPE win; uint32_t games = 0, overflow=0, illegal = 0; uint32_t road_wins=0, flat_wins=0, road_turns=0, flat_turns=0, diff --git a/src/tei.c b/src/tei.c new file mode 100644 index 0000000..93c67ce --- /dev/null +++ b/src/tei.c @@ -0,0 +1,163 @@ +/* + ctaktei, a TEI interface to the ctak library & its computer opponent + + Copyright (C) 2021, tslil clingman + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <tak.h> +#include <tps.h> +#include <negamax.h> + +// Set up output function for negamax +inline void +negamax_display_progress(const uint8_t cur_depth, + const uint8_t init_depth, + const uint32_t length) { + (void)(cur_depth); + (void)(init_depth); + (void)(length); +} + +enum TEI_RETURN { TEI_QUIT, TEI_OK, TEI_FAILURE }; + +// expects ``(startpos|tps <tps>) moves <ptn>'' +static enum TEI_RETURN +parse_position_string(char *line) { + // Find the word ``moves'' + char *beg = strstr(line, "moves"); + + if (!strncasecmp(line, "tps", 3)) { + // Split the string on the space before moves + if (beg) *(beg-1) = 0; + // so that we can load it as a TPS description + load_tps(line + 4); + } else if (!strncasecmp(line, "startpos", 8)) { + reset_state(board_size); + } else { + return TEI_FAILURE; + } + + if (beg == NULL) return TEI_OK; + + // parse the PTN sequence + strtok(beg, " "); + char *ptn = strtok(NULL, " "); + while (ptn != NULL) { + if (do_ptn(ptn) != ACT_OK) return TEI_FAILURE; + ptn = strtok(NULL, " "); + } + + return TEI_OK; +} + +static enum TEI_RETURN +handle_tei(char *line) { + if (!strcmp(line, "quit")) { + return TEI_QUIT; + } if (!strcmp(line, "isready")) { + puts("readyok"); + } else if (!strncmp(line, "setoption Depth value ", 22)) { + negamax_search_depth = atoi(line + 23); + } else if (!strncmp(line, "go", 2)) { + // TODO: for now we ignore all of the parameters + float minimax = negamax_generate(); + enum ACT_RESULT r = do_ptn(negamax_ptn); + if (r != ACT_OK && r != GAME_END) return TEI_FAILURE; + printf("info score cp %f pv %s\nbestmove %s\n", + minimax, negamax_ptn, negamax_ptn); + } else if (!strncmp(line, "position", 8)) { + return parse_position_string(line + 9); + } else if (!strncmp(line, "teinewgame", 10)) { + if (line[11] != '5') return TEI_FAILURE; + const uint8_t size = line[11] - '0'; + if (size != board_size) { + negamax_free(); + reset_state(size); + negamax_init(size); + } else { + reset_state(size); + } + } + fflush(stdout); + return TEI_OK; +} + +const char* license = "ctaktei, a TEI interface to the ctak library & its computer opponent\n\ +\n\ +Copyright (C) 2021, tslil clingman\n\ +\n\ +This program comes with ABSOLUTELY NO WARRANTY; and is made available under the terms of the GNU GPL v3 license. This is free software, and you are welcome to redistribute it under certain conditions; see COPYING for details.\n"; + +int main(int argc, char **argv) { + (void)(argc); + (void)(argv); + + puts(license); + + char *line = NULL; + ssize_t read = -1; + size_t alloc_size; + + // Wait for tei + while ((read = getline(&line, &alloc_size, stdin))) { + if (read > 0 && !strncmp("tei", line, 3)) { + break; + } else return EXIT_FAILURE; + } + + if (line) free(line); + line = NULL; + + // Identify ourselves, and send the options + puts("id name ct1986"); + puts("id author tslil clingman"); + puts("option name Depth type spin default 4 min 2 max 6"); + puts("teiok"); + fflush(stdout); + + // Set default option + negamax_search_depth = 4; + negamax_init(5); + reset_state(5); + + for (int playing = 1; playing;) { + if ((read = getline(&line, &alloc_size, stdin)) > 0) { + line[read-1] = 0; + switch (handle_tei(line)) { + case TEI_FAILURE: return EXIT_FAILURE; + case TEI_QUIT: playing = 0; // fall-through + case TEI_OK: { + if (line) { + free(line); + line = NULL; + } + break; + } + } + } else { + break; + } + } + + if (line) free(line); + negamax_free(); + + return EXIT_SUCCESS; +} |
