aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortslil clingman <tslil@posteo.de>2023-01-15 21:31:00 +0100
committertslil <tslil@posteo.de>2026-08-28 19:37:41 +0100
commit0223a9bec5535fced1a7698b55fd42155d9b0446 (patch)
treee7a980454e65d88b56194eed733cabec29ef51b5 /src
parentee216c008a188a9436fedb85c70ee5d1719733b1 (diff)
switch to explicit game state & important bug fix & clang format
Previously the code base assumed that there was a single, global game state which was the implicit target of all actions taken. Looking ahead at architectural improvements, this has now been (almost entirely) made explicit and functions take tak_state_p where necessary (and also where unnecessary). Two important fixes to actions.c were made: - Previously when generating the possible stack moves, stack height overflows (> 15) were not taken into account and this resulted in the tree search corrupting the board state. Now action search does not list all legal actions, rather the subset of these encodeable by the implementation. - The check for crushing on a stack move was incorrect (too strict), and this resulted in many legitimate moves being igonored. Finally, in other changes, weights have also been improved by training all games instead of some subset for chosen players, and clang-format was run on the codebase.
Diffstat (limited to 'src')
-rw-r--r--src/ct1986.c260
-rw-r--r--src/ctlm.c240
-rw-r--r--src/cttei.c96
-rw-r--r--src/geminict.c203
-rw-r--r--src/nn_train.py10
-rw-r--r--src/pptdb.c390
6 files changed, 636 insertions, 563 deletions
diff --git a/src/ct1986.c b/src/ct1986.c
index f9046ce..8a49ec1 100644
--- a/src/ct1986.c
+++ b/src/ct1986.c
@@ -23,47 +23,46 @@
#include <ncurses.h>
-#include <tak.h>
-#include <negamax.h>
#include <lcdlib.h>
-
+#include <negamax.h>
+#include <tak.h>
static int human;
static char *gamelog = 0;
-static void
-new_game(uint8_t size) {
- reset_state(size);
- lcd_printf_line(L_SCROLL, "New 5s game @ D%d",
- negamax_search_depth);
- if (gamelog) gamelog = realloc(gamelog, sizeof(char));
- else gamelog = malloc(sizeof(char));
+tak_state_p state;
+
+static void new_game(uint8_t size) {
+ reset_state(state, size);
+ lcd_printf_line(L_SCROLL, "New 5s game @ D%d", negamax_search_depth);
+ if (gamelog)
+ gamelog = realloc(gamelog, sizeof(char));
+ else
+ gamelog = malloc(sizeof(char));
gamelog[0] = 0;
}
-static int
-append_to_gamelog(const char *line, const uint8_t win_line) {
+static int append_to_gamelog(const char *line, const uint8_t win_line) {
// I _could_ dynamically compute the size but ... don't let
// `perfect' be the enemy of `good' ?
- if (gamelog == NULL) return EXIT_FAILURE;
+ if (gamelog == NULL)
+ return EXIT_FAILURE;
char prepend[8];
if (win_line) {
prepend[0] = '\n';
prepend[1] = 0;
- } else if (ply & 1) {
- snprintf(prepend, 7, "%s%d. ",
- (ply == 1) ? "" : "\n", ply/2+1);
+ } else if (state->ply & 1) {
+ snprintf(prepend, 7, "%s%d. ", (state->ply == 1) ? "" : "\n",
+ state->ply / 2 + 1);
} else {
strcpy(prepend, " ");
}
// Make room for this line
- gamelog = realloc(gamelog,
- strlen(gamelog)
- + strlen(prepend)
- + strlen(line) + 1);
+ gamelog =
+ realloc(gamelog, strlen(gamelog) + strlen(prepend) + strlen(line) + 1);
// TODO: trap errno
strcat(gamelog, prepend);
strcat(gamelog, line);
@@ -71,127 +70,151 @@ append_to_gamelog(const char *line, const uint8_t win_line) {
return EXIT_SUCCESS;
}
-static void
-end_game(char *line, char *win) {
+static void end_game(char *line, char *win) {
append_to_gamelog(line, 0);
append_to_gamelog(win, 1);
lcd_printf_line(L_SCROLL, "Game over: %s", win);
}
-static int
-handle_turn(char *line) {
+static int handle_turn(char *line) {
// Track win state
- uint8_t new_win = (won == 0xFF);
- switch (do_ptn(line)) {
- // Errors
- 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?
- if (new_win) {
- switch (won) {
- case WIN_DRAW: { end_game(line,"1/2-1/2"); break; }
- case WIN_FLAT_BLACK: { end_game(line,"0-F"); break; }
- case WIN_FLAT_WHITE: { end_game(line,"F-0"); break; }
- case WIN_ROAD_BLACK: { end_game(line,"0-R"); break; }
- case WIN_ROAD_WHITE: { end_game(line,"R-0"); break; }
- }
- return EXIT_SUCCESS;
- } else {
- lcd_put_line(L_SCROLL, "Game over.");
- break;
+ uint8_t new_win = (state->won == 0xFF);
+ switch (do_ptn(state, line)) {
+ // Errors
+ case ACT_INVALID_PTN: {
+ lcd_put_line(L_SCROLL, "Invalid PTN.");
+ break;
+ }
+ case ACT_ILLEGAL: {
+ lcd_put_line(L_SCROLL, "Illegal state->ply.");
+ break;
+ }
+ case ACT_OVERFLOW: {
+ lcd_put_line(L_SCROLL, "Overflow.");
+ break;
+ }
+ // Game has ended
+ case GAME_END: {
+ // Did it end this turn?
+ if (new_win) {
+ switch (state->won) {
+ case WIN_DRAW: {
+ end_game(line, "1/2-1/2");
+ break;
+ }
+ case WIN_FLAT_BLACK: {
+ end_game(line, "0-F");
+ break;
+ }
+ case WIN_FLAT_WHITE: {
+ end_game(line, "F-0");
+ break;
+ }
+ case WIN_ROAD_BLACK: {
+ end_game(line, "0-R");
+ break;
+ }
+ case WIN_ROAD_WHITE: {
+ end_game(line, "R-0");
+ break;
+ }
}
- }
- // Valid, append to game log
- default: {
- append_to_gamelog(line, 0);
return EXIT_SUCCESS;
+ } else {
+ lcd_put_line(L_SCROLL, "Game over.");
+ break;
}
}
+ // Valid, append to game log
+ default: {
+ append_to_gamelog(line, 0);
+ return EXIT_SUCCESS;
+ }
+ }
return EXIT_FAILURE;
}
-
// Set up output function for negamax
static uint32_t perc;
-inline void
-negamax_display_progress(const uint8_t depth, const uint32_t length) {
- if (depth == negamax_search_depth) {
- lcd_printf_line(L_OVERWRITE, "Computing: %d%%",
- (++perc*100)/length);
+inline void negamax_display_progress(const uint8_t cur_depth,
+ const uint8_t init_depth,
+ const uint32_t length) {
+ (void)init_depth;
+ if (cur_depth == negamax_search_depth) {
+ lcd_printf_line(L_OVERWRITE, "Computing: %d%%", (++perc * 100) / length);
}
}
-static int
-negamax_turn() {
+static int negamax_turn() {
// Prepare progress bar
perc = 0;
lcd_put_line(L_SCROLL, "Computing: 0%");
// Run the minimax
- float minimax = negamax_generate();
+ float minimax = negamax_generate(state);
// Failed to find a move?
if (minimax < -infty) {
lcd_printf_line(L_SCROLL, "%s concedes!",
- (ply & 1) ? "Black" : "White");
+ (state->ply & 1) ? "Black" : "White");
return EXIT_FAILURE;
} else {
- lcd_printf_line(L_SCROLL, "%s: %s",
- (ply & 1) ? "Black" : "White",
- negamax_ptn);
+ lcd_printf_line(L_SCROLL, "%s: %s", (state->ply & 1) ? "Black" : "White",
+ negamax_ptn);
return handle_turn(negamax_ptn);
}
}
-static void
-do_game_log(void) {
- lcd_put_line(L_SCROLL, "TODO!");
-}
+static void do_game_log(void) { lcd_put_line(L_SCROLL, "TODO!"); }
-static int
-input_is_not_turn(const char *line) {
+static int input_is_not_turn(const char *line) {
switch (line[0]) {
- case 'l': { do_game_log(); break; }
- case 'n': { new_game(5); break; }
- case 'D': {
- negamax_search_depth = line[1] - '0';
- lcd_printf_line(L_SCROLL, "Search depth: %d",
- negamax_search_depth);
- break;
- }
- case 'B': { human = 1; new_game(5); break; }
- case 'W': { human = 0; new_game(5); break; }
- default: return EXIT_FAILURE;
+ case 'l': {
+ do_game_log();
+ break;
+ }
+ case 'n': {
+ new_game(5);
+ break;
+ }
+ case 'D': {
+ negamax_search_depth = line[1] - '0';
+ lcd_printf_line(L_SCROLL, "Search depth: %d", negamax_search_depth);
+ break;
+ }
+ case 'B': {
+ human = 1;
+ new_game(5);
+ break;
+ }
+ case 'W': {
+ human = 0;
+ new_game(5);
+ break;
+ }
+ default:
+ return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
-const char* license = "ct1986, an interface to the ct library designed to be embedded on a Raspberry Pi Zero\n\
+const char *license =
+ "ct1986, an interface to the ct library designed to be embedded on a Raspberry Pi Zero\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";
#define LINE_BUFF_LEN 9
-static char line[LINE_BUFF_LEN+1];
+static char line[LINE_BUFF_LEN + 1];
-static int
-poll_input(void) {
+static int poll_input(void) {
char c, *prompt;
int idx = 0, polling = 1;
- if (ply & 1) prompt = "Black: ";
- else prompt = "White: ";
+ if (state->ply & 1)
+ prompt = "Black: ";
+ else
+ prompt = "White: ";
lcd_put_line(L_SCROLL, prompt);
@@ -200,24 +223,25 @@ poll_input(void) {
lcd_printf_line(L_OVERWRITE, "%s%s", prompt, line);
c = getchar();
switch (c) {
- case 0x7F: {
- if (idx>0) idx--;
- line[idx] = 0;
- break;
- }
- case 0xFF: // fall-through
- case '\r': // fall-through
- case '\n': {
- polling = 0;
- break;
- }
- default: {
- if (idx+1<LINE_BUFF_LEN) {
- line[idx] = c;
- line[++idx] = 0;
- }
- break;
+ case 0x7F: {
+ if (idx > 0)
+ idx--;
+ line[idx] = 0;
+ break;
+ }
+ case 0xFF: // fall-through
+ case '\r': // fall-through
+ case '\n': {
+ polling = 0;
+ break;
+ }
+ default: {
+ if (idx + 1 < LINE_BUFF_LEN) {
+ line[idx] = c;
+ line[++idx] = 0;
}
+ break;
+ }
}
}
return idx;
@@ -243,15 +267,15 @@ int main(int argc, char **argv) {
while (human == 0) {
lcd_set_blink();
if (poll_input() > 0) {
- if (input_is_not_turn(line)) {
- int r = handle_turn(line);
- if (r == EXIT_SUCCESS && won == 0xFF) {
- human = 1;
- }
- }
+ if (input_is_not_turn(line)) {
+ int r = handle_turn(line);
+ if (r == EXIT_SUCCESS && state->won == 0xFF) {
+ human = 1;
+ }
+ }
} else {
- playing = 0;
- human = 1;
+ playing = 0;
+ human = 1;
}
}
lcd_stop_blink();
diff --git a/src/ctlm.c b/src/ctlm.c
index a8bc194..fa27274 100644
--- a/src/ctlm.c
+++ b/src/ctlm.c
@@ -17,6 +17,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+#include "actions.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -47,13 +48,13 @@ static void put_stone(const enum STONE_VARIANT stone, const enum COLOUR colour,
}
if (top) {
switch (stone) {
- case STONE_FLAT:
+ case STONE_FLAT:
putchar(CHAR_FLT);
break;
- case STONE_STANDING:
+ case STONE_STANDING:
putchar(CHAR_STN);
break;
- case STONE_CAPSTONE:
+ case STONE_CAPSTONE:
putchar(CHAR_CAP);
break;
}
@@ -63,11 +64,11 @@ static void put_stone(const enum STONE_VARIANT stone, const enum COLOUR colour,
fputs(rst, stdout);
}
-static void print_cell_line(const uint8_t line, const uint8_t col,
- const uint8_t row) {
+static void print_cell_line(tak_state_p state, const uint8_t line,
+ const uint8_t col, const uint8_t row) {
- const uint8_t location = THE_COORDS(col, row),
- stack_size = COUNT_AT(location);
+ const uint8_t location = THE_COORDS(state->board_size, col, row),
+ stack_size = COUNT_AT(state, location);
uint8_t idx;
for (uint8_t k = 0; k < SQUARE_W; k++) {
@@ -85,9 +86,9 @@ static void print_cell_line(const uint8_t line, const uint8_t col,
}
}
if (idx < stack_size) {
- put_stone(STONE_AT(location),
- (colours[location] & (1 << idx)) ? C_BLACK : C_WHITE, idx == 0,
- idx >= board_size);
+ put_stone(STONE_AT(state, location),
+ (state->colours[location] & (1 << idx)) ? C_BLACK : C_WHITE,
+ idx == 0, idx >= state->board_size);
} else {
putchar(' ');
}
@@ -96,9 +97,9 @@ static void print_cell_line(const uint8_t line, const uint8_t col,
// It takes board_size*(SQUARE_H+1)+1 lines to print the board, they
// may be requested in any order and at any time
-static void print_board_line(const uint8_t line) {
+static void print_board_line(tak_state_p state, const uint8_t line) {
const uint8_t mod = line % (SQUARE_H + 1),
- row = board_size - line / (SQUARE_H + 1) - 1;
+ row = state->board_size - line / (SQUARE_H + 1) - 1;
// Print leader, either row number if half-way through square or
// padding spaces otherwise
@@ -109,7 +110,7 @@ static void print_board_line(const uint8_t line) {
// Top and bottom of squares receive borders
if (mod == 0) {
- for (uint8_t x = 0; x < board_size; x++) {
+ for (uint8_t x = 0; x < state->board_size; x++) {
putchar('+');
for (uint8_t k = 0; k < SQUARE_W; k++)
putchar('-');
@@ -117,15 +118,15 @@ static void print_board_line(const uint8_t line) {
puts("+");
} else {
// Interior of board should be filled by borders and pieces
- if (line < board_size * (SQUARE_H + 1)) {
- for (uint8_t x = 0; x < board_size; x++) {
+ if (line < state->board_size * (SQUARE_H + 1)) {
+ for (uint8_t x = 0; x < state->board_size; x++) {
putchar('|');
- print_cell_line(mod - 1, x, row);
+ print_cell_line(state, mod - 1, x, row);
}
puts("|");
} else {
// Bottom of board has column markers
- for (uint8_t x = 0; x < board_size; x++) {
+ for (uint8_t x = 0; x < state->board_size; x++) {
for (uint8_t k = 0; k <= SQUARE_W / 2; k++)
putchar(' ');
printf("%c.", x + 'a');
@@ -138,47 +139,54 @@ static void print_board_line(const uint8_t line) {
}
// Simple wrapper to print the whole board in one go
-static void print_board(void) {
- for (uint8_t k = 0; k < board_size * (SQUARE_H + 1) + 2; k++) {
- print_board_line(k);
+static void print_board(tak_state_p state) {
+ for (uint8_t k = 0; k < state->board_size * (SQUARE_H + 1) + 2; k++) {
+ print_board_line(state, k);
}
}
// Print the contents of a single square
-static void print_square(const uint8_t col, const uint8_t row) {
- if (row < board_size && col < board_size) {
+static void print_square(tak_state_p state, const uint8_t col,
+ const uint8_t row) {
+ if (row < state->board_size && col < state->board_size) {
printf("%c%c: ", 'a' + col, '1' + row);
- const uint8_t stack_size = COUNT_AT(THE_COORDS(col, row));
+ const uint8_t stack_size =
+ COUNT_AT(state, THE_COORDS(state->board_size, col, row));
if (stack_size > 0) {
uint8_t mask = 1 << (stack_size - 1);
for (uint8_t k = 0; k < stack_size; k++, mask >>= 1) {
- put_stone(STONE_AT(THE_COORDS(col, row)),
- (colours[THE_COORDS(col, row)] & mask) ? C_BLACK : C_WHITE,
- k + 1 == stack_size, stack_size - k - 1 >= board_size);
+ put_stone(
+ STONE_AT(state, THE_COORDS(state->board_size, col, row)),
+ (state->colours[THE_COORDS(state->board_size, col, row)] & mask)
+ ? C_BLACK
+ : C_WHITE,
+ k + 1 == stack_size, stack_size - k - 1 >= state->board_size);
}
puts(" <-- top");
} else {
puts("(empty)");
}
} else {
- printf("Requested square not on board (%dx%d).\n", board_size, board_size);
+ printf("Requested square not on board (%dx%d).\n", state->board_size,
+ state->board_size);
}
}
-static void print_info(void) {
- printf("Turn: %2d, %s%s%s%s\n", ply / 2 + 1, (ply & 1) ? blk : wht,
- (ply & 1) ? "Black" : "White", rst,
- (ply < 2) ? " (counter-play start)" : "");
+static void print_info(tak_state_p state) {
+ printf("Turn: %2d, %s%s%s%s\n", state->ply / 2 + 1,
+ (state->ply & 1) ? blk : wht, (state->ply & 1) ? "Black" : "White",
+ rst, (state->ply < 2) ? " (counter-play start)" : "");
printf("Flats/Caps remaining: %s%02d/%d%s, %s%02d/%d%s\n", wht,
- white_count & 127, white_count >> 7, rst, blk, black_count & 127,
- black_count >> 7, rst);
+ state->white_count & 127, state->white_count >> 7, rst, blk,
+ state->black_count & 127, state->black_count >> 7, rst);
}
static int human;
static char *gamelog = NULL;
static uint8_t auto_board = 0xFF, auto_info = 0xFF;
-static int append_to_gamelog(const char *line, const uint8_t win_line) {
+static int append_to_gamelog(tak_state_p state, const char *line,
+ const uint8_t win_line) {
// I _could_ dynamically compute the size but ... don't let
// `perfect' be the enemy of `good' ?
@@ -190,14 +198,15 @@ static int append_to_gamelog(const char *line, const uint8_t win_line) {
if (win_line) {
prepend[0] = '\n';
prepend[1] = 0;
- } else if (ply & 1) {
- snprintf(prepend, 7, "%s%d. ", (ply == 1) ? "" : "\n", ply / 2 + 1);
+ } else if (state->ply & 1) {
+ snprintf(prepend, 7, "%s%d. ", (state->ply == 1) ? "" : "\n",
+ state->ply / 2 + 1);
} else {
strcpy(prepend, " ");
}
// Make room for this line
gamelog =
- realloc(gamelog, strlen(gamelog) + strlen(prepend) + strlen(line) + 1);
+ realloc(gamelog, strlen(gamelog) + strlen(prepend) + strlen(line) + 1);
// TODO: trap errno
strcat(gamelog, prepend);
strcat(gamelog, line);
@@ -205,79 +214,79 @@ static int append_to_gamelog(const char *line, const uint8_t win_line) {
return EXIT_SUCCESS;
}
-static void end_game(char *line, char *win) {
- append_to_gamelog(line, 0);
- append_to_gamelog(win, 1);
- print_board();
+static void end_game(tak_state_p state, char *line, char *win) {
+ append_to_gamelog(state, line, 0);
+ append_to_gamelog(state, win, 1);
+ print_board(state);
puts("Game over:");
puts(gamelog);
putchar('\n');
}
-static int handle_turn(char *line) {
+static int handle_turn(tak_state_p state, char *line) {
// Track win state
- uint8_t new_win = (won == 0xFF);
- switch (do_ptn(line)) {
- // Errors
- case ACT_INVALID_PTN: {
- puts("Invalid PTN.");
- return EXIT_FAILURE;
- }
- case ACT_ILLEGAL: {
- puts("Illegal action.");
- return EXIT_FAILURE;
- }
- case ACT_OVERFLOW: {
- puts("Move would cause internal overflow, select another.");
- return EXIT_FAILURE;
- }
- // Game has ended
- case GAME_END: {
- // Did it end this turn?
- if (new_win) {
- switch (won) {
+ uint8_t new_win = (state->won == 0xFF);
+ switch (do_ptn(state, line)) {
+ // Errors
+ case ACT_INVALID_PTN: {
+ puts("Invalid PTN.");
+ return EXIT_FAILURE;
+ }
+ case ACT_ILLEGAL: {
+ puts("Illegal action.");
+ return EXIT_FAILURE;
+ }
+ case ACT_OVERFLOW: {
+ puts("Move would cause internal overflow, select another.");
+ return EXIT_FAILURE;
+ }
+ // Game has ended
+ case GAME_END: {
+ // Did it end this turn?
+ if (new_win) {
+ switch (state->won) {
case WIN_DRAW: {
- end_game(line, "1/2-1/2");
+ end_game(state, line, "1/2-1/2");
break;
}
case WIN_FLAT_BLACK: {
- end_game(line, "0-F");
+ end_game(state, line, "0-F");
break;
}
case WIN_FLAT_WHITE: {
- end_game(line, "F-0");
+ end_game(state, line, "F-0");
break;
}
case WIN_ROAD_BLACK: {
- end_game(line, "0-R");
+ end_game(state, line, "0-R");
break;
}
case WIN_ROAD_WHITE: {
- end_game(line, "R-0");
+ end_game(state, line, "R-0");
break;
}
- }
}
- puts("Enter `new' to play again.");
- if (!new_win)
- return EXIT_FAILURE;
- break;
- }
- // Valid, append to game log
- case ACT_OK: {
- append_to_gamelog(line, 0);
- if (auto_board)
- print_board();
- if (auto_info)
- print_info();
- break;
}
+ puts("Enter `new' to play again.");
+ if (!new_win)
+ return EXIT_FAILURE;
+ break;
+ }
+ // Valid, append to game log
+ case ACT_OK: {
+ append_to_gamelog(state, line, 0);
+ if (auto_board)
+ print_board(state);
+ if (auto_info)
+ print_info(state);
+ break;
+ }
}
return EXIT_SUCCESS;
}
-static void new_game(uint8_t size) {
- reset_state(size);
+static void new_game(tak_state_p state, uint8_t size) {
+ reset_state(state, size);
printf("New %dx%d game! negamax at search depth %d.\n", size, size,
negamax_search_depth);
if (gamelog)
@@ -287,7 +296,7 @@ static void new_game(uint8_t size) {
gamelog[0] = 0;
}
-static int load_ptn(const char *fn) {
+static int load_ptn(tak_state_p state, const char *fn) {
FILE *fh = NULL;
fh = fopen(fn, "r");
@@ -316,14 +325,14 @@ static int load_ptn(const char *fn) {
space2++;
line[space2] = 0;
// Try the first piece we found
- r = handle_turn(line + space1);
+ r = handle_turn(state, line + space1);
if (r) {
printf("Error on: %s\n", line + space1);
break;
}
// If there's a second piece, try it
if (space2 + 1 < read) {
- r = handle_turn(line + space2 + 1);
+ r = handle_turn(state, line + space2 + 1);
if (r) {
printf("Error on: %s", line + space2 + 1);
break;
@@ -362,37 +371,37 @@ inline void negamax_display_progress(const uint8_t cur_depth,
}
}
-static int negamax_turn(void) {
- if (won == 0xFF) {
+static int negamax_turn(tak_state_p state) {
+ if (state->won == 0xFF) {
// Run the minimax
num_check = 0;
progress = 0;
old_depth = 0;
- float minimax = negamax_generate();
+ float minimax = negamax_generate(state);
putchar('\n');
// Failed to find a non-losing move?
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);
+ return handle_turn(state, negamax_ptn);
} else {
return EXIT_FAILURE;
}
}
-static int input_is_not_turn(const char *line) {
+static int input_is_not_turn(tak_state_p state, const char *line) {
if (!strcmp(line, "help")) {
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();
+ print_board(state);
} else if (!strcmp(line, "info")) {
- print_info();
+ print_info(state);
} else if (!strcmp(line, "eval")) {
- float eval = nn1986_evaluate_black_win() * 100;
- if (ply & 1) {
+ float eval = nn1986_evaluate_black_win(state) * 100;
+ if (state->ply & 1) {
printf("Black heuristic chance: %s%.2f%s\n", blk, eval, rst);
} else {
printf("White heruistic chance: %s%.2f%s\n", wht, -eval, rst);
@@ -400,13 +409,13 @@ help, info, load <file.ptn>, log, new, play (b|w), self-play, square\
} else if (!strcmp(line, "log")) {
puts(gamelog);
} else if (!strcmp(line, "new")) {
- new_game(5);
+ new_game(state, 5);
} else if (!strcmp(line, "tps")) {
char buf[1000];
- generate_tps(buf);
+ generate_tps(state, buf);
puts(buf);
} else if (!strcmp(line, "self-play")) {
- while (negamax_turn() == 0)
+ while (negamax_turn(state) == 0)
;
} else if (!strncmp(line, "depth", 5)) {
if (strnlen(line, 7) == 7 && line[6] >= '0' && line[6] <= '9') {
@@ -417,7 +426,7 @@ help, info, load <file.ptn>, log, new, play (b|w), self-play, square\
}
} else if (!strncmp(line, "load", 4)) {
if (strnlen(line, 6) >= 6) {
- if (load_ptn(line + 5)) {
+ if (load_ptn(state, line + 5)) {
printf("Errors in file %s\n", line);
}
} else {
@@ -437,19 +446,19 @@ help, info, load <file.ptn>, log, new, play (b|w), self-play, square\
}
} else if (!strncmp(line, "square", 6)) {
if (strnlen(line, 10) == 9 && line[7] >= 'a' &&
- line[7] <= '`' + board_size && line[8] >= '1' &&
- line[8] <= '0' + board_size) {
- print_square(line[7] - 'a', line[8] - '1');
+ line[7] <= '`' + state->board_size && line[8] >= '1' &&
+ line[8] <= '0' + state->board_size) {
+ print_square(state, line[7] - 'a', line[8] - '1');
} else {
- printf("Usage: square [a-%c][1-%c].\n", '`' + board_size,
- '0' + board_size);
+ printf("Usage: square [a-%c][1-%c].\n", '`' + state->board_size,
+ '0' + state->board_size);
}
} else if (!strncmp(line, "play", 4)) {
if (strnlen(line, 7) == 6 && ((line[5] == 'b' || line[5] == 'B') ||
(line[5] == 'w' || line[5] == 'W'))) {
// 'b' is even :)
human = 1 - (line[5] & 1);
- new_game(5);
+ new_game(state, 5);
} else {
puts("Usage: play (b|w).");
}
@@ -471,19 +480,12 @@ int main(int argc, char **argv) {
puts(license);
+ tak_state_p state = new_tak_state(5);
+
negamax_search_depth = 5;
- new_game(5);
+ new_game(state, 5);
negamax_init(5);
- // Test harness
- if (argc > 1) {
- negamax_search_depth = 7;
- load_ptn("data/0.ptn");
- negamax_turn();
- return 0;
- }
- // Test harness
-
char *line = NULL;
ssize_t read = -1;
size_t alloc_size;
@@ -496,9 +498,9 @@ int main(int argc, char **argv) {
read = getline(&line, &alloc_size, stdin);
if (read > 0) {
line[read - 1] = 0;
- if (input_is_not_turn(line)) {
- int r = handle_turn(line);
- if (r == EXIT_SUCCESS && won == 0xFF) {
+ if (input_is_not_turn(state, line)) {
+ int r = handle_turn(state, line);
+ if (r == EXIT_SUCCESS && state->won == 0xFF) {
human = 1;
}
}
@@ -510,7 +512,7 @@ int main(int argc, char **argv) {
}
}
if (playing) {
- negamax_turn();
+ negamax_turn(state);
human = 0;
}
}
diff --git a/src/cttei.c b/src/cttei.c
index 534faef..f7fafa6 100644
--- a/src/cttei.c
+++ b/src/cttei.c
@@ -17,19 +17,18 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include <negamax.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) {
+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);
@@ -38,68 +37,71 @@ negamax_display_progress(const uint8_t cur_depth,
enum TEI_RETURN { TEI_QUIT, TEI_OK, TEI_FAILURE };
// expects ``(startpos|tps <tps>) moves <ptn>''
-static enum TEI_RETURN
-parse_position_string(char *line) {
+static enum TEI_RETURN parse_position_string(tak_state_p state, 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;
+ if (beg)
+ *(beg - 1) = 0;
// so that we can load it as a TPS description
- load_tps(line + 4);
+ load_tps(state, line + 4);
} else if (!strncasecmp(line, "startpos", 8)) {
- reset_state(board_size);
+ reset_state(state, 5);
} else {
return TEI_FAILURE;
}
- if (beg == NULL) return TEI_OK;
+ 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;
+ if (do_ptn(state, ptn) != ACT_OK)
+ return TEI_FAILURE;
ptn = strtok(NULL, " ");
}
return TEI_OK;
}
-static enum TEI_RETURN
-handle_tei(char *line) {
+static enum TEI_RETURN handle_tei(tak_state_p state, char *line) {
if (!strcmp(line, "quit")) {
return TEI_QUIT;
- } if (!strcmp(line, "isready")) {
+ }
+ 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);
+ float minimax = negamax_generate(state);
+ enum ACT_RESULT r = do_ptn(state, 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);
+ return parse_position_string(state, line + 9);
} else if (!strncmp(line, "teinewgame", 10)) {
- if (line[11] != '5') return TEI_FAILURE;
+ if (line[11] != '5')
+ return TEI_FAILURE;
const uint8_t size = atoi(line + 11);
- if (size != board_size) {
- negamax_free();
- reset_state(size);
- negamax_init(size);
+ if (size != 5) {
+ return TEI_FAILURE;
} else {
- reset_state(size);
+ reset_state(state, 5);
}
}
fflush(stdout);
return TEI_OK;
}
-const char* license = "cttei, a TEI interface to the ct library & its computer opponent\n\
+const char *license =
+ "cttei, a TEI interface to the ct library & its computer opponent\n\
\n\
Copyright (C) 2021, tslil clingman\n\
\n\
@@ -119,44 +121,50 @@ int main(int argc, char **argv) {
while ((read = getline(&line, &alloc_size, stdin))) {
if (read > 0 && !strncmp("tei", line, 3)) {
break;
- } else return EXIT_FAILURE;
+ } else
+ return EXIT_FAILURE;
}
- if (line) free(line);
+ if (line)
+ free(line);
line = NULL;
// Identify ourselves, and send the options
- puts("id name cttei_dense");
+ puts("id name cttei");
puts("id author tslil clingman");
puts("option name Depth type spin default 4 min 2 max 6");
puts("teiok");
fflush(stdout);
// Set default option
+ tak_state_p state = new_tak_state(5);
negamax_search_depth = 4;
negamax_init(5);
- reset_state(5);
+ reset_state(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;
+ line[read - 1] = 0;
+ switch (handle_tei(state, line)) {
+ case TEI_FAILURE:
+ return EXIT_FAILURE;
+ case TEI_QUIT:
+ playing = 0; // fall-through
+ case TEI_OK: {
+ if (line) {
+ free(line);
+ line = NULL;
+ }
+ break;
}
- break;
- }
}
} else {
break;
}
}
- if (line) free(line);
+ if (line)
+ free(line);
negamax_free();
return EXIT_SUCCESS;
diff --git a/src/geminict.c b/src/geminict.c
index 387c219..6f2d04b 100644
--- a/src/geminict.c
+++ b/src/geminict.c
@@ -39,6 +39,8 @@
#define STR_CHF_BLK "b"
#define STR_CHF_WHT "w"
+static tak_state_p state;
+
static void put_stone(const enum STONE_VARIANT stone, const enum COLOUR colour,
const uint8_t top, const uint8_t beyond_carry_limit) {
if (beyond_carry_limit) {
@@ -46,18 +48,18 @@ static void put_stone(const enum STONE_VARIANT stone, const enum COLOUR colour,
} else {
if (top) {
switch (stone) {
- case STONE_FLAT: {
- fputs((colour == C_BLACK) ? STR_FLT_BLK : STR_FLT_WHT, stdout);
- break;
- }
- case STONE_STANDING: {
- fputs((colour == C_BLACK) ? STR_STN_BLK : STR_STN_WHT, stdout);
- break;
- }
- case STONE_CAPSTONE: {
- fputs((colour == C_BLACK) ? STR_CAP_BLK : STR_CAP_WHT, stdout);
- break;
- }
+ case STONE_FLAT: {
+ fputs((colour == C_BLACK) ? STR_FLT_BLK : STR_FLT_WHT, stdout);
+ break;
+ }
+ case STONE_STANDING: {
+ fputs((colour == C_BLACK) ? STR_STN_BLK : STR_STN_WHT, stdout);
+ break;
+ }
+ case STONE_CAPSTONE: {
+ fputs((colour == C_BLACK) ? STR_CAP_BLK : STR_CAP_WHT, stdout);
+ break;
+ }
}
} else {
fputs((colour == C_BLACK) ? STR_HFL_BLK : STR_HFL_WHT, stdout);
@@ -68,8 +70,8 @@ static void put_stone(const enum STONE_VARIANT stone, const enum COLOUR colour,
static void print_cell_line(const uint8_t line, const uint8_t col,
const uint8_t row) {
- const uint8_t location = THE_COORDS(col, row),
- stack_size = COUNT_AT(location);
+ const uint8_t location = THE_COORDS(5, col, row),
+ stack_size = COUNT_AT(state, location);
uint8_t idx;
for (uint8_t k = 0; k < SQUARE_W; k++) {
@@ -87,20 +89,20 @@ static void print_cell_line(const uint8_t line, const uint8_t col,
}
}
if (idx < stack_size) {
- put_stone(STONE_AT(location),
- (colours[location] & (1 << idx)) ? C_BLACK : C_WHITE, idx == 0,
- idx >= board_size);
+ put_stone(STONE_AT(state, location),
+ (state->colours[location] & (1 << idx)) ? C_BLACK : C_WHITE,
+ idx == 0, idx >= 5);
} else {
putchar(' ');
}
}
}
-// It takes board_size*(SQUARE_H+1)+1 lines to print the board, they
+// It takes 5*(SQUARE_H+1)+1 lines to print the board, they
// may be requested in any order and at any time
static void print_board_line(const uint8_t line) {
const uint8_t mod = line % (SQUARE_H + 1),
- row = board_size - line / (SQUARE_H + 1) - 1;
+ row = 5 - line / (SQUARE_H + 1) - 1;
// Print leader, either row number if half-way through square or
// padding spaces otherwise
@@ -111,7 +113,7 @@ static void print_board_line(const uint8_t line) {
// Top and bottom of squares receive borders
if (mod == 0) {
- for (uint8_t x = 0; x < board_size; x++) {
+ for (uint8_t x = 0; x < 5; x++) {
putchar('+');
for (uint8_t k = 0; k < SQUARE_W; k++)
putchar('-');
@@ -119,15 +121,15 @@ static void print_board_line(const uint8_t line) {
puts("+");
} else {
// Interior of board should be filled by borders and pieces
- if (line < board_size * (SQUARE_H + 1)) {
- for (uint8_t x = 0; x < board_size; x++) {
+ if (line < 5 * (SQUARE_H + 1)) {
+ for (uint8_t x = 0; x < 5; x++) {
putchar('|');
print_cell_line(mod - 1, x, row);
}
puts("|");
} else {
// Bottom of board has column markers
- for (uint8_t x = 0; x < board_size; x++) {
+ for (uint8_t x = 0; x < 5; x++) {
for (uint8_t k = 0; k <= SQUARE_W / 2; k++)
putchar(' ');
printf("%c.", x + 'a');
@@ -141,19 +143,19 @@ static void print_board_line(const uint8_t line) {
// Simple wrapper to print the whole board in one go
static void print_board(void) {
- for (uint8_t k = 0; k < board_size * (SQUARE_H + 1) + 2; k++) {
+ for (uint8_t k = 0; k < 5 * (SQUARE_H + 1) + 2; k++) {
print_board_line(k);
}
putchar('\n');
}
static void print_info(void) {
- printf("Turn: %2d, %s%s\n", ply / 2 + 1,
- (ply & 1) ? "Black" : "White",
- (ply < 2) ? " (counter-play start)" : "");
- printf("Flats/Caps remaining: %02d/%d, %02d/%d\n",
- white_count & 127, white_count >> 7, black_count & 127,
- black_count >> 7);
+ printf("Turn: %2d, %s%s\n", state->ply / 2 + 1,
+ (state->ply & 1) ? "Black" : "White",
+ (state->ply < 2) ? " (counter-play start)" : "");
+ printf("Flats/Caps remaining: %02d/%d, %02d/%d\n", state->white_count & 127,
+ state->white_count >> 7, state->black_count & 127,
+ state->black_count >> 7);
}
static char *gamelog = NULL;
@@ -176,14 +178,15 @@ static int append_to_gamelog(const char *line, const uint8_t win_line) {
if (win_line) {
prepend[0] = '\n';
prepend[1] = 0;
- } else if (ply & 1) {
- snprintf(prepend, 7, "%s%d. ", (ply == 1) ? "" : "\n", ply / 2 + 1);
+ } else if (state->ply & 1) {
+ snprintf(prepend, 7, "%s%d. ", (state->ply == 1) ? "" : "\n",
+ state->ply / 2 + 1);
} else {
strcpy(prepend, " ");
}
// Make room for this line
gamelog =
- realloc(gamelog, strlen(gamelog) + strlen(prepend) + strlen(line) + 1);
+ realloc(gamelog, strlen(gamelog) + strlen(prepend) + strlen(line) + 1);
// TODO: trap errno
strcat(gamelog, prepend);
strcat(gamelog, line);
@@ -203,64 +206,64 @@ static void end_game(char *line, char *win) {
enum TURN_RESULT { T_ERR, T_OK, T_WIN };
static enum TURN_RESULT handle_turn(char *line) {
// Track win state
- uint8_t new_win = (won == 0xFF);
- switch (do_ptn(line)) {
- // Errors
- case ACT_INVALID_PTN: {
- puts("Invalid PTN.");
- return T_ERR;
- }
- case ACT_ILLEGAL: {
- puts("Illegal action.");
- return T_ERR;
- }
- case ACT_OVERFLOW: {
- puts("Move would cause internal overflow, select another.");
- return T_ERR;
- }
- // Game has ended
- case GAME_END: {
- // Did it end this turn?
- if (new_win) {
- switch (won) {
- case WIN_DRAW: {
- end_game(line, "1/2-1/2");
- break;
- }
- case WIN_FLAT_BLACK: {
- end_game(line, "0-F");
- break;
- }
- case WIN_FLAT_WHITE: {
- end_game(line, "F-0");
- break;
- }
- case WIN_ROAD_BLACK: {
- end_game(line, "0-R");
- break;
- }
- case WIN_ROAD_WHITE: {
- end_game(line, "R-0");
- break;
- }
- }
- return T_WIN;
+ uint8_t new_win = (state->won == 0xFF);
+ switch (do_ptn(state, line)) {
+ // Errors
+ case ACT_INVALID_PTN: {
+ puts("Invalid PTN.");
+ return T_ERR;
+ }
+ case ACT_ILLEGAL: {
+ puts("Illegal action.");
+ return T_ERR;
+ }
+ case ACT_OVERFLOW: {
+ puts("Move would cause internal overflow, select another.");
+ return T_ERR;
+ }
+ // Game has ended
+ case GAME_END: {
+ // Did it end this turn?
+ if (new_win) {
+ switch (state->won) {
+ case WIN_DRAW: {
+ end_game(line, "1/2-1/2");
+ break;
}
- if (!new_win)
- return T_ERR;
- break;
- }
- // Valid, append to game log
- case ACT_OK: {
- append_to_gamelog(line, 0);
- break;
+ case WIN_FLAT_BLACK: {
+ end_game(line, "0-F");
+ break;
+ }
+ case WIN_FLAT_WHITE: {
+ end_game(line, "F-0");
+ break;
+ }
+ case WIN_ROAD_BLACK: {
+ end_game(line, "0-R");
+ break;
+ }
+ case WIN_ROAD_WHITE: {
+ end_game(line, "R-0");
+ break;
+ }
+ }
+ return T_WIN;
}
+ if (!new_win)
+ return T_ERR;
+ break;
+ }
+ // Valid, append to game log
+ case ACT_OK: {
+ append_to_gamelog(line, 0);
+ break;
+ }
}
return T_OK;
}
static void new_game(uint8_t size) {
- reset_state(size);
+ reset_state(state, size);
if (gamelog)
gamelog = realloc(gamelog, sizeof(char));
else
@@ -281,16 +284,16 @@ inline void negamax_display_progress(const uint8_t cur_depth,
}
static enum TURN_RESULT negamax_turn(void) {
- if (won == 0xFF) {
+ if (state->won == 0xFF) {
// Run the minimax
num_check = 0;
- float minimax = negamax_generate();
+ float minimax = negamax_generate(state);
putchar('\n');
// Failed to find a non-losing move?
if (minimax <= -infty)
puts("Opponent concedes!");
- printf("ct1986 says: %s (minmax %.2f, checked %.1e)\n\n", negamax_ptn, minimax * 100.0,
- num_check);
+ printf("ct1986 says: %s (minmax %.2f, checked %.1e)\n\n", negamax_ptn,
+ minimax * 100.0, num_check);
return handle_turn(negamax_ptn);
} else {
return T_ERR;
@@ -303,13 +306,14 @@ int main(int argc, char **argv) {
return EXIT_FAILURE;
};
+ state = new_tak_state(5);
negamax_search_depth = 5;
new_game(5);
negamax_init(5);
enum TURN_RESULT tr;
- char* line = argv[1];
+ char *line = argv[1];
// Some maximum length we're willing to parse
uint32_t len = strnlen(line, 65535);
if (!line || len < 2 || len == 65535) {
@@ -318,7 +322,7 @@ int main(int argc, char **argv) {
}
// strip quotes
- if (line[0]=='\'') {
+ if (line[0] == '\'') {
line++;
len--;
} else {
@@ -326,8 +330,8 @@ int main(int argc, char **argv) {
return EXIT_FAILURE;
}
- if (line[len-1]=='\'') {
- line[len-1]=0;
+ if (line[len - 1] == '\'') {
+ line[len - 1] = 0;
len--;
} else {
puts("Malformed input.");
@@ -337,7 +341,8 @@ int main(int argc, char **argv) {
uint32_t start = 0, end = 0;
while (start < len) {
// Find first separator
- while (end < len && line[end] != '.') end++;
+ while (end < len && line[end] != '.')
+ end++;
// If still on line
if (end < len) {
// Mark the split
@@ -345,10 +350,11 @@ int main(int argc, char **argv) {
// Try the first piece we found
tr = handle_turn(line + start);
if (tr == T_ERR) {
- printf("Error on: %s\n", line + start);
- print_everything();
- return EXIT_FAILURE;
- } else if (tr == T_WIN) return EXIT_SUCCESS;
+ printf("Error on: %s\n", line + start);
+ print_everything();
+ return EXIT_FAILURE;
+ } else if (tr == T_WIN)
+ return EXIT_SUCCESS;
start = ++end;
} else {
puts("Malformed input.");
@@ -362,7 +368,8 @@ int main(int argc, char **argv) {
puts("This shouldn't happen, ct1986 encountered an error.");
print_everything();
return EXIT_FAILURE;
- } else if (tr == T_WIN) return EXIT_SUCCESS;
+ } else if (tr == T_WIN)
+ return EXIT_SUCCESS;
print_everything();
diff --git a/src/nn_train.py b/src/nn_train.py
index 12ce868..f2550b4 100644
--- a/src/nn_train.py
+++ b/src/nn_train.py
@@ -56,10 +56,10 @@ def train(size, model, data, iterations=1, epochs=10, batch=None):
tra_res = model.evaluate(tra_input, tra_outcome, verbose=False)
results.append((tra_res, val_res))
print(val_res)
- write_weights(model, i+1, (tra_res, val_res))
+ write_weights(model, str(i+1).zfill(len(str(iterations))), (tra_res, val_res))
print("\nScores")
for i, data in enumerate(results):
- print(f"Iteration {i}: {data}")
+ print(f"Iteration {i+1}: {data}")
return results
@@ -84,12 +84,12 @@ def write_weights(model, iteration, performance):
output_weights = transpose(model.trainable_variables[2], perm=[1, 0])
output_bias = model.trainable_variables[3]
# Prepare output
- to_output = [("dense1_weights[DENSE_NUM][INP_NUM]",dense1_weights)
+ to_output = [("dense1_weights[DENSE_NUM][INP_NUM]", dense1_weights),
("dense1_biases[DENSE_NUM]", dense1_biases),
("output_weights[2][DENSE_NUM]", output_weights),
("output_bias[2]", output_bias)]
# Write to file
- f = open("weights-"+str(iteration)+".txt", "w")
+ f = open("weights-"+iteration+".txt", "w")
f.write("/*\n")
model.summary(print_fn=lambda l: f.write(" * "+l+"\n"))
f.write(" * "+str(performance)+"\n*/\n\n")
@@ -103,4 +103,4 @@ data = load_data(5)
model = make_model(5, 64)
print("Before training", model.evaluate(data[1][0], data[1][1], verbose=False, batch_size=16))
-results = train(5, model, data, iterations=1, epochs=10, batch=None)
+results = train(5, model, data, iterations=5, epochs=10, batch=128)
diff --git a/src/pptdb.c b/src/pptdb.c
index 42e8706..cdc0004 100644
--- a/src/pptdb.c
+++ b/src/pptdb.c
@@ -30,210 +30,242 @@ FILE *training_fh = NULL;
float max_flats;
uint8_t outcome_black;
-static void
-write_input(const int dx, const int dy, const uint8_t swap) {
- // Two numbers for flats remaining
- fprintf(training_fh,"%d,%.8f,%.8f,",
- ply & 1 ? 1 : -1,
- (float)(white_count & 127)/max_flats,
- (float)(black_count & 127)/max_flats);
-
- // Write the board layers
- float val;
- int col, row;
- row = (dy>0)?-1:board_size;
- for (int i = 0; i < board_size; i++) {
- row += dy;
- col = (dx>0)?-1:board_size;
- for (int j = 0; j < board_size; j++) {
- col += dx;
- const uint8_t k =
- (swap) ? THE_COORDS(row, col) : THE_COORDS(col, row);
- val = 0;
- if (COUNT_AT(k)>0) {
- // Top layer of stacks is handled differently to indicate
- // stone type
- if (STONE_AT(k) == STONE_STANDING) {
- val = (colours[k] & 1) ? +0.25 : -0.25;
- } else if (STONE_AT(k) == STONE_CAPSTONE) {
- val = (colours[k] & 1) ? +1.00 : -1.00;
- } else {
- val = (colours[k] & 1) ? +0.50 : -0.50;
- } }
- fprintf(training_fh,"%.2f,", val);
+tak_state_p state;
+
+static void write_input(const int dx, const int dy, const uint8_t swap) {
+ // Two numbers for flats remaining
+ fprintf(training_fh, "%d,%.8f,%.8f,", state->ply & 1 ? 1 : -1,
+ (float)(state->white_count & 127) / max_flats,
+ (float)(state->black_count & 127) / max_flats);
+
+ // Write the board layers
+ float val;
+ int col, row;
+ row = (dy > 0) ? -1 : state->board_size;
+ for (int i = 0; i < state->board_size; i++) {
+ row += dy;
+ col = (dx > 0) ? -1 : state->board_size;
+ for (int j = 0; j < state->board_size; j++) {
+ col += dx;
+ const uint8_t k = (swap) ? THE_COORDS(state->board_size, row, col)
+ : THE_COORDS(state->board_size, col, row);
+ val = 0;
+ if (COUNT_AT(state, k) > 0) {
+ // Top layer of stacks is handled differently to indicate
+ // stone type
+ if (STONE_AT(state, k) == STONE_STANDING) {
+ val = (state->colours[k] & 1) ? +0.25 : -0.25;
+ } else if (STONE_AT(state, k) == STONE_CAPSTONE) {
+ val = (state->colours[k] & 1) ? +1.00 : -1.00;
+ } else {
+ val = (state->colours[k] & 1) ? +0.50 : -0.50;
}
+ }
+ fprintf(training_fh, "%.2f,", val);
}
- fprintf(training_fh, "%d,%d\n", outcome_black ? 1 : 0, outcome_black ? 0 : 1);
+ }
+ fprintf(training_fh, "%d,%d\n", outcome_black ? 1 : 0, outcome_black ? 0 : 1);
}
// Warning: performs _no_ checks on input whatsoever
-static enum ACT_RESULT
-parse_line(const char *pt, const ssize_t read) {
- ssize_t idx;
- enum ACT_RESULT r;
- int total_plies = 0;
-
- for (idx=0;idx<read;idx++) {
- if (pt[idx]==',') total_plies++;
- }
- for(idx=0;;) {
- if (pt[idx] == 'P') {
- // P [A-F][1-6] [CF]?,
- idx+=2;
- enum STONE_VARIANT stone;
- const uint8_t col = pt[idx]-'A', row = pt[idx+1]-'1';
-
- if (idx + 3 < read) {
- switch (pt[idx+3]) {
- case 'W': { stone = STONE_STANDING; break; }
- case 'C': { stone = STONE_CAPSTONE; break; }
- default: { stone = STONE_FLAT; break; }
- }
- } else {
- stone = STONE_FLAT;
- }
-
- r = try_place(THE_COORDS(col,row), current_colour, stone);
- if (r != ACT_OK) return r;
- } else if (pt[idx] == 'M') {
- // M [A-F][1-6] [A-F][1-6]( [1-6])+,
- idx+=2;
- uint8_t drops[board_size];
- const uint8_t s_col =pt[idx]-'A', s_row=pt[idx+1]-'1',
- d_col=pt[idx+3]-'A', d_row=pt[idx+4]-'1';
- idx+=4;
-
- enum MOVE_DIRECTION dir = M_RIGHT;
- if (s_col < d_col) dir=M_RIGHT;
- else if (s_col > d_col) dir=M_LEFT;
- else if (s_row < d_row) dir=M_UP;
- else if (s_row > d_row) dir=M_DOWN;
-
- uint8_t steps = 0;
- do {
- idx+=2;
- drops[steps++] = pt[idx] - '0';
- } while (idx+2<read && pt[idx+1] != ',');
-
- r = try_move(THE_COORDS(s_col, s_row), dir, steps, drops);
-
- if (r != ACT_OK) return r;
-
- if (generate == 0) {
- // Measure height of stacks exceeding 1
- for (int k = 0; k < board_size * board_size; k++) {
- if (COUNT_AT(k)>1) heights[COUNT_AT(k)]+=1;
- }
- }
+static enum ACT_RESULT parse_line(const char *pt, const ssize_t read) {
+ ssize_t idx;
+ enum ACT_RESULT r;
+ int total_plies = 0;
+
+ for (idx = 0; idx < read; idx++) {
+ if (pt[idx] == ',')
+ total_plies++;
+ }
+ for (idx = 0;;) {
+ if (pt[idx] == 'P') {
+ // P [A-F][1-6] [CF]?,
+ idx += 2;
+ enum STONE_VARIANT stone;
+ const uint8_t col = pt[idx] - 'A', row = pt[idx + 1] - '1';
+
+ if (idx + 3 < read) {
+ switch (pt[idx + 3]) {
+ case 'W': {
+ stone = STONE_STANDING;
+ break;
+ }
+ case 'C': {
+ stone = STONE_CAPSTONE;
+ break;
}
- // Generate training data, not too early in the game and not at
- // the end, under all eight symmetries of the board
- if (generate && ply > 7 && ply < total_plies && ply + 10 >= total_plies) {
- write_input(+1, +1, 1); write_input(+1, +1, 0);
- write_input(+1, -1, 1); write_input(+1, -1, 0);
- write_input(-1, +1, 1); write_input(-1, +1, 0);
- write_input(-1, -1, 1); write_input(-1, -1, 0);
+ default: {
+ stone = STONE_FLAT;
+ break;
}
- // Parse next action
- while (idx<read && pt[idx++]!=',');
- if (idx>=read) return ACT_OK;
- next_ply();
+ }
+ } else {
+ stone = STONE_FLAT;
+ }
+
+ r = try_place(state, THE_COORDS(state->board_size, col, row),
+ state->current_colour, stone);
+ if (r != ACT_OK)
+ return r;
+ } else if (pt[idx] == 'M') {
+ // M [A-F][1-6] [A-F][1-6]( [1-6])+,
+ idx += 2;
+ uint8_t drops[state->board_size];
+ const uint8_t s_col = pt[idx] - 'A', s_row = pt[idx + 1] - '1',
+ d_col = pt[idx + 3] - 'A', d_row = pt[idx + 4] - '1';
+ idx += 4;
+
+ enum MOVE_DIRECTION dir = M_RIGHT;
+ if (s_col < d_col)
+ dir = M_RIGHT;
+ else if (s_col > d_col)
+ dir = M_LEFT;
+ else if (s_row < d_row)
+ dir = M_UP;
+ else if (s_row > d_row)
+ dir = M_DOWN;
+
+ uint8_t steps = 0;
+ do {
+ idx += 2;
+ drops[steps++] = pt[idx] - '0';
+ } while (idx + 2 < read && pt[idx + 1] != ',');
+
+ r = try_move(state, THE_COORDS(state->board_size, s_col, s_row), dir,
+ steps, drops);
+
+ if (r != ACT_OK)
+ return r;
+
+ if (generate == 0) {
+ // Measure height of stacks exceeding 1
+ for (int k = 0; k < state->board_size * state->board_size; k++) {
+ if (COUNT_AT(state, k) > 1)
+ heights[COUNT_AT(state, k)] += 1;
+ }
+ }
+ }
+ // Generate training data, not too early in the game and not at
+ // the end, under all eight symmetries of the board
+ if (generate && state->ply > 7 && state->ply < total_plies &&
+ state->ply + 10 >= total_plies) {
+ write_input(+1, +1, 1);
+ write_input(+1, +1, 0);
+ write_input(+1, -1, 1);
+ write_input(+1, -1, 0);
+ write_input(-1, +1, 1);
+ write_input(-1, +1, 0);
+ write_input(-1, -1, 1);
+ write_input(-1, -1, 0);
}
- return ACT_OK;
+ // Parse next action
+ while (idx < read && pt[idx++] != ',')
+ ;
+ if (idx >= read)
+ return ACT_OK;
+ next_ply(state);
+ }
+ return ACT_OK;
}
-const char* license = "pptdb, generate neural network training data from a playtak.com database dump\n\
+const char *license =
+ "pptdb, generate neural network training data from a playtak.com database dump\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);
-
- 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,
- white_wins = 0, black_wins = 0;
-
- for (int k = 0; k < 16; k++) heights[k] = 0;
-
- size_t len = 0;
- ssize_t read = 0;
- FILE *playtak_fh = NULL;
- char *line = NULL, td_fn[65];
-
- const uint8_t size = argv[1][0]-'0';
-
- playtak_fh = fopen(argv[2], "r");
- if (playtak_fh == NULL) exit(EXIT_FAILURE);
-
- if (argc > 3 && (!strncmp("generate", argv[3], 8))) {
- generate=1;
- max_flats = (size == 5) ? 21.0 : 30.0;
- snprintf(td_fn, 64, "data/parsed-%d.csv",size);
- training_fh = fopen(td_fn, "w");
- if (training_fh == NULL) exit(EXIT_FAILURE);
- } else generate=0;
-
- while ((read = getline(&line, &len, playtak_fh)) != -1) {
- // Reset everything
- reset_state(size);
- // Store the outcome of this game. Black win = 1
- outcome_black = (line[read-4] == '0');
- // Parse the line
- r = parse_line(line,read-4);
- // Adjust counts if we're not generating training data
- if (generate == 0) {
- if (r == ACT_ILLEGAL) {
- illegal++;
- printf("Illegal:\n%s",line);
- } else if (r == ACT_OVERFLOW) {
- printf("Overflow:\n%s",line);
- overflow++;
- } else {
- win = check_win();
- if (win == WIN_FLAT_BLACK
- || win == WIN_FLAT_WHITE
- || win == WIN_DRAW) {
- flat_wins++;
- flat_turns += ply/2+1;
- } else {
- road_wins++;
- road_turns += ply/2+1;
- }
- if (win == WIN_FLAT_BLACK || win == WIN_ROAD_BLACK)
- black_wins++;
- else if (win == WIN_FLAT_WHITE || win == WIN_ROAD_WHITE)
- white_wins++;
- }
+ (void)(argc);
+
+ 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,
+ white_wins = 0, black_wins = 0;
+
+ for (int k = 0; k < 16; k++)
+ heights[k] = 0;
+
+ size_t len = 0;
+ ssize_t read = 0;
+ FILE *playtak_fh = NULL;
+ char *line = NULL, td_fn[65];
+
+ const uint8_t size = argv[1][0] - '0';
+
+ playtak_fh = fopen(argv[2], "r");
+ if (playtak_fh == NULL)
+ exit(EXIT_FAILURE);
+
+ if (argc > 3 && (!strncmp("generate", argv[3], 8))) {
+ generate = 1;
+ max_flats = (size == 5) ? 21.0 : 30.0;
+ snprintf(td_fn, 64, "data/parsed-%d.csv", size);
+ training_fh = fopen(td_fn, "w");
+ if (training_fh == NULL)
+ exit(EXIT_FAILURE);
+ } else
+ generate = 0;
+
+ state = new_tak_state(size);
+ while ((read = getline(&line, &len, playtak_fh)) != -1) {
+ // Reset everything
+ reset_state(state, size);
+ // Store the outcome of this game. Black win = 1
+ outcome_black = (line[read - 4] == '0');
+ // Parse the line
+ r = parse_line(line, read - 4);
+ // Adjust counts if we're not generating training data
+ if (generate == 0) {
+ if (r == ACT_ILLEGAL) {
+ illegal++;
+ printf("Illegal:\n%s", line);
+ } else if (r == ACT_OVERFLOW) {
+ printf("Overflow:\n%s", line);
+ overflow++;
+ } else {
+ win = check_win(state);
+ if (win == WIN_FLAT_BLACK || win == WIN_FLAT_WHITE || win == WIN_DRAW) {
+ flat_wins++;
+ flat_turns += state->ply / 2 + 1;
+ } else {
+ road_wins++;
+ road_turns += state->ply / 2 + 1;
}
- games++;
+ if (win == WIN_FLAT_BLACK || win == WIN_ROAD_BLACK)
+ black_wins++;
+ else if (win == WIN_FLAT_WHITE || win == WIN_ROAD_WHITE)
+ white_wins++;
+ }
}
+ games++;
+ }
- fclose(playtak_fh);
- if (generate) fclose(training_fh);
- if (line) free(line);
+ fclose(playtak_fh);
+ if (generate)
+ fclose(training_fh);
+ if (line)
+ free(line);
- if (illegal || overflow) putchar('\n');
- printf("Read %d games\n",games);
+ if (illegal || overflow)
+ putchar('\n');
+ printf("Read %d games\n", games);
- if (generate==0) {
- printf("Illegals: %d\nOverflows: %d\n\
+ if (generate == 0) {
+ printf("Illegals: %d\nOverflows: %d\n\
Black wins: %.3f%%\n\
Road wins: %d\nFlat wins: %d\n\
Average turns to road win: %.3f\n\
Average turns to flat win: %.3f\n",
- illegal, overflow,
- (double)black_wins / (double)(black_wins+white_wins) * 100,
- road_wins, flat_wins,
- (double)(road_turns)/(double)(road_wins),
- (double)(flat_turns)/(double)(flat_wins));
- for (int k = 2; k < 16; k++) {
- printf("Height %2d: %7ld\n",k,heights[k]);
- }
+ illegal, overflow,
+ (double)black_wins / (double)(black_wins + white_wins) * 100,
+ road_wins, flat_wins, (double)(road_turns) / (double)(road_wins),
+ (double)(flat_turns) / (double)(flat_wins));
+ for (int k = 2; k < 16; k++) {
+ printf("Height %2d: %7ld\n", k, heights[k]);
}
+ }
- exit(EXIT_SUCCESS);
+ exit(EXIT_SUCCESS);
}