diff options
Diffstat (limited to 'src/ctlm.c')
| -rw-r--r-- | src/ctlm.c | 240 |
1 files changed, 121 insertions, 119 deletions
@@ -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; } } |
