aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
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.c360
6 files changed, 621 insertions, 548 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);
+tak_state_p state;
- // 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);
+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;
+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';
+ 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;
- }
+ 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;
+ 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;
+ 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] != ',');
+ 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);
+ r = try_move(state, THE_COORDS(state->board_size, s_col, s_row), dir,
+ steps, drops);
- if (r != ACT_OK) return r;
+ 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;
- }
- }
- }
- // 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);
+ 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;
}
- // Parse next action
- while (idx<read && pt[idx++]!=',');
- if (idx>=read) return ACT_OK;
- next_ply();
+ }
}
- return ACT_OK;
+ // 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);
+ }
+ // 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);
+ (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;
+ 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;
+ 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];
+ 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';
+ const uint8_t size = argv[1][0] - '0';
- playtak_fh = fopen(argv[2], "r");
- if (playtak_fh == NULL) exit(EXIT_FAILURE);
+ 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;
+ 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++;
- }
+ 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);
}