aboutsummaryrefslogtreecommitdiff
path: root/src/ctlm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ctlm.c')
-rw-r--r--src/ctlm.c240
1 files changed, 121 insertions, 119 deletions
diff --git a/src/ctlm.c b/src/ctlm.c
index a8bc194..fa27274 100644
--- a/src/ctlm.c
+++ b/src/ctlm.c
@@ -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;
}
}