/* pptdb, generate neural network training data from a playtak.com database dump Copyright (C) 2021, tslil clingman This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include int generate; uint64_t heights[16]; FILE *training_fh = NULL; float max_flats, outcome_black; static void write_input(const int dx, const int dy, const uint8_t swap) { // Two numbers for flats remaining fprintf(training_fh,"%.8f,%.8f,", (float)(white_count & 127)/max_flats, (float)(black_count & 127)/max_flats); // Write the board layers float val; int col, row; for (uint8_t depth = 0; depth < board_size + 1; depth++) { 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)>depth) { if (depth == 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.75 : -0.75; } } else { // Layers underneath val = (colours[k] & (1<length); action_list_free(actions); 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+21) 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 < total_plies && ply + 2 >= 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) goto count_this; next_ply(); } count_this: avg_actions /= (float) total_plies; *out_avg_actions = avg_actions; return ACT_OK; } 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'; #define LAZY_MAX_GAMES 1000000 float avg_actions[LAZY_MAX_GAMES]; for (uint32_t k=0; k 3 && (!strncmp("generate", argv[3], 8))) { generate=1; max_flats = (size == 5) ? 21.0 : 30.0; snprintf(td_fn, 64, "data/training-%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 if (line[read-4] == '0') outcome_black = 0.9; else outcome_black = -0.9; // Parse the line r = parse_line(line,read-4, avg_actions+games); // 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++; } } games++; } fclose(playtak_fh); if (generate) fclose(training_fh); if (line) free(line); if (illegal || overflow) putchar('\n'); printf("Read %d games\n",games); if (generate==0) { float sum = 0; for (uint32_t k=0; k