/* ct1986, an interface to the ct library designed to be embedded on a Raspberry Pi Zero 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 static int human; static char *gamelog = 0; static struct tak_state_s state_s; static tak_state_p state = &state_s; 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) { // I _could_ dynamically compute the size but ... don't let // `perfect' be the enemy of `good' ? if (gamelog == NULL) return EXIT_FAILURE; char prepend[8]; if (win_line) { prepend[0] = '\n'; prepend[1] = 0; } 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); // TODO: trap errno strcat(gamelog, prepend); strcat(gamelog, line); return EXIT_SUCCESS; } 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) { // Track win state 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; } } 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 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() { // Prepare progress bar perc = 0; lcd_put_line(L_SCROLL, "Computing: 0%"); // Run the minimax float minimax = negamax_generate(state); // Failed to find a move? if (minimax < -infty) { lcd_printf_line(L_SCROLL, "%s concedes!", (state->ply & 1) ? "Black" : "White"); return EXIT_FAILURE; } else { 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 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; } return EXIT_SUCCESS; } 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 int poll_input(void) { char c, *prompt; int idx = 0, polling = 1; if (state->ply & 1) prompt = "Black: "; else prompt = "White: "; lcd_put_line(L_SCROLL, prompt); line[0] = 0; while (polling) { 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 < LINE_BUFF_LEN) { line[idx] = c; line[++idx] = 0; } break; } } } return idx; } int main(int argc, char **argv) { (void)(argc); (void)(argv); puts(license); if (lcd_begin() != EXIT_SUCCESS) return EXIT_FAILURE; initscr(); negamax_search_depth = 3; new_game(5); negamax_init(5); human = 0; for (int playing = 1; playing;) { 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 && state->won == 0xFF) { human = 1; } } } else { playing = 0; human = 1; } } lcd_stop_blink(); if (playing) { negamax_turn(); human = 0; } } free(gamelog); negamax_free(); lcd_end(); endwin(); return EXIT_SUCCESS; }