From 0223a9bec5535fced1a7698b55fd42155d9b0446 Mon Sep 17 00:00:00 2001 From: tslil clingman Date: Sun, 15 Jan 2023 21:31:00 +0100 Subject: 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. --- src/ct1986.c | 260 ++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 142 insertions(+), 118 deletions(-) (limited to 'src/ct1986.c') 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 -#include -#include #include - +#include +#include 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 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(); -- cgit v1.2.3