aboutsummaryrefslogtreecommitdiff
path: root/src/geminict.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/geminict.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/geminict.c')
-rw-r--r--src/geminict.c203
1 files changed, 105 insertions, 98 deletions
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();