aboutsummaryrefslogtreecommitdiff
path: root/src/pptdb.c
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/pptdb.c
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/pptdb.c')
-rw-r--r--src/pptdb.c390
1 files changed, 211 insertions, 179 deletions
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);
}