#include #include #include #include #include const char* esc = "\x1B[L"; FILE *lcd = NULL; static char *gamelog = 0; static int human; static void write_line(const char *line) { fprintf(lcd, "%s\n", line); fflush(lcd); } static void 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' ? 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 { strcpy(prepend, " "); } // Make room for this line gamelog = realloc(gamelog, strlen(gamelog) + strlen(prepend) + strlen(line) + 1); strcat(gamelog,prepend); strcat(gamelog,line); } static void end_game(char *line, char *win) { append_to_gamelog(line, 0); append_to_gamelog(win, 1); fprintf(lcd, "Game over: %s\n", win); fflush(lcd); } static int handle_turn(char *line) { // Track win state uint8_t new_win = (won == 0xFF); switch (do_ptn(line)) { // Errors case PTN_INVALID: { write_line("Invalid PTN."); break; } case ACT_ILLEGAL: { write_line("Illegal ply."); break; } case ACT_OVERFLOW: { write_line("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 { write_line("Game over."); break; } } // Valid, append to game log default: { append_to_gamelog(line, 0); return EXIT_SUCCESS; } } return EXIT_FAILURE; } static void new_game(uint8_t size) { reset_state(size); write_line("New 5x5 game."); if (gamelog) gamelog = realloc(gamelog, sizeof(char)); else gamelog = malloc(sizeof(char)); gamelog[0] = 0; } // Set up output function for negamax_cnn1986 static uint8_t perc; inline void negamax_cnn1986_display_progress(const uint8_t depth) { if (depth == 0) { perc++; // back four spaces and clear line, followed by perc% fprintf(lcd, "%sl%sl%sl%sl%sk%3d%%", esc, esc, esc, esc, esc, perc*4); fflush(lcd); } } static int negamax_cnn1986_turn() { // Prepare progress bar fputs("Computing: ", lcd); fflush(lcd); // Run the minimax perc = 0; float minimax = negamax_cnn1986_generate(); fputc('\n', lcd); fflush(lcd); // Failed to find a move? if (minimax <= -infty) { fprintf(lcd, "%s concedes!\n", (ply & 1) ? "Black" : "White"); return EXIT_FAILURE; } else { fprintf(lcd, "%s: %s\n", (ply & 1) ? "Black" : "White", negamax_cnn1986_ptn); handle_turn(negamax_cnn1986_ptn); return EXIT_SUCCESS; } } static void do_game_log(void) { fputs(gamelog, lcd); } 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_cnn1986_search_depth = line[1] - '0'; fprintf(lcd, "Search depth: %d\n", negamax_cnn1986_search_depth); fflush(lcd); break; } case 'b': { human = 1; new_game(5); break; } case 'w': { human = 0; new_game(5); break; } default: return EXIT_FAILURE; } return EXIT_SUCCESS; } int main(int argc, char **argv) { (void)(argc); (void)(argv); lcd = fopen("/dev/lcd", "w"); if (lcd == NULL) return EXIT_FAILURE; human = 0; negamax_cnn1986_search_depth = 3; new_game(5); fprintf(lcd, "%sB", esc); fflush(lcd); char *line = NULL; ssize_t read; size_t alloc_size; for (int playing = 1; playing;) { while (human == 0) { if (ply & 1) write_line("Black: "); else write_line("White: "); 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) { human = 1; } } } else if (read == -1) { playing = 0; break; } } negamax_cnn1986_turn(); human = 0; } fclose(lcd); return EXIT_SUCCESS; }