/*
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;
}