diff options
| author | Mattias Andrée <maandree@kth.se> | 2021-04-22 20:49:48 +0200 |
|---|---|---|
| committer | Mattias Andrée <maandree@kth.se> | 2021-04-22 20:51:38 +0200 |
| commit | 77e516665f23b9cb7af864318c61582d91ab5870 (patch) | |
| tree | 50772907d8d9ce249e47e034455489e22185ac98 /tic-tac-toe.c | |
| download | basic-games-77e516665f23b9cb7af864318c61582d91ab5870.tar.gz basic-games-77e516665f23b9cb7af864318c61582d91ab5870.tar.bz2 basic-games-77e516665f23b9cb7af864318c61582d91ab5870.tar.xz | |
First commit, with implementation of tic-tac-toe
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to 'tic-tac-toe.c')
| -rw-r--r-- | tic-tac-toe.c | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/tic-tac-toe.c b/tic-tac-toe.c new file mode 100644 index 0000000..907ff94 --- /dev/null +++ b/tic-tac-toe.c @@ -0,0 +1,664 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +USAGE(""); + +static volatile sig_atomic_t caught_sigterm = 0; +static volatile sig_atomic_t caught_sigwinch = 1; + +static struct termios saved_stty; + +static int cpu_begins = 0; +static int rand_on_first = 0; +static int level = 5; + + + +static void +sigterm(int signo) +{ + caught_sigterm = 1; + (void) signo; +} + +static void +sigwinch(int signo) +{ + caught_sigwinch = 1; + (void) signo; +} + + +static void +restore_terminal(void) +{ + printf("\033[?25h\n\033[?1049;1003;1006l"); + fflush(stdout); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty); +} + + +static void +get_win_combination(char board[3][3], char marks[3][3]) +{ + int i; + memset(marks, 0, sizeof(char[3][3])); + for (i = 0; i < 3; i++) { + if (board[i][0] != ' ' && board[i][0] == board[i][1] && board[i][1] == board[i][2]) + marks[i][0] = marks[i][1] = marks[i][2] = 1; + if (board[0][i] != ' ' && board[0][i] == board[1][i] && board[1][i] == board[2][i]) + marks[0][i] = marks[1][i] = marks[2][i] = 1; + } + if (board[1][1] != ' ' && board[0][0] == board[1][1] && board[1][1] == board[2][2]) + marks[0][0] = marks[1][1] = marks[2][2] = 1; + if (board[1][1] != ' ' && board[0][2] == board[1][1] && board[1][1] == board[2][0]) + marks[0][2] = marks[1][1] = marks[2][0] = 1; +} + + +static void +get_winner(char board[3][3], char *winnerp) +{ + int i; + for (i = 0; i < 3; i++) { + if (board[i][0] != ' ' && board[i][0] == board[i][1] && board[i][1] == board[i][2]) { + *winnerp = board[i][0]; + return; + } + if (board[0][i] != ' ' && board[0][i] == board[1][i] && board[1][i] == board[2][i]) { + *winnerp = board[0][i]; + return; + } + } + if (board[1][1] != ' ' && board[0][0] == board[1][1] && board[1][1] == board[2][2]) { + *winnerp = board[1][1]; + return; + } + if (board[1][1] != ' ' && board[0][2] == board[1][1] && board[1][1] == board[2][0]) { + *winnerp = board[1][1]; + return; + } + *winnerp = ' '; +} + + +static int16_t +cpu_think(char board[3][3], char player, char cpu_player, int empty, int depth, int maximize) +{ + int16_t score, choices = 0, best = maximize ? -111 : 111; + char winner; + int x, y; + if (empty == 0) + return 0; + if (!depth) { + for (y = 0; y < 3; y++) + for (x = 0; x < 3; x++) + if (board[y][x] == ' ') + choices |= (int16_t)(1 << (y * 3 + x)); + return (int16_t)(empty | choices); + } + for (y = 0; y < 3; y++) { + for (x = 0; x < 3; x++) { + if (board[y][x] != ' ') + continue; + board[y][x] = player; + get_winner(board, &winner); + if (winner == cpu_player) { + score = (int16_t)(20 - empty); + } else if (winner != ' ') { + score = (int16_t)(empty - 20); + } else { + score = cpu_think(board, player ^ 'X' ^ 'O', cpu_player, empty - 1, depth - 1, maximize ^ 1); + score >>= 9; + } + if (maximize ? (score >= best) : (score <= best)) { + if (score != best) { + best = score; + choices = 0; + } + choices |= (int16_t)(1 << (y * 3 + x)); + } + board[y][x] = ' '; + } + } + return (int16_t)((best << 9) | choices); +} + + +static void +cpu_play(char board[3][3], char player, int empty, int rand_move) +{ + int16_t packed_choices = cpu_think(board, player, player, empty, rand_move ? 0 : level, 1); + int16_t bit; + char choices[9], i; + int nchoices = 0, y, x; + long int r, mask; + for (bit = 1, i = 0; i < 9; i++, bit <<= 1) + if (packed_choices & bit) + choices[nchoices++] = i; + if (nchoices == 0) + abort(); + if (nchoices == 1) + r = 0; + else { + if (nchoices <= 2) + mask = 1; + else if (nchoices <= 4) + mask = 3; + else if (nchoices <= 8) + mask = 7; + else + mask = 15; + do { + r = random() & mask; + } while (r >= nchoices); + } + i = choices[r]; + y = i / 3; + x = i % 3; + board[y][x] = player; +} + + +int +main(int argc, char *argv[]) +{ + union libterminput_input input; + struct libterminput_state termctx; + struct termios stty; + struct sigaction sigact; + struct winsize winsize = {.ws_col = 80, .ws_row = 24}; + int r, dualplayer = 0, redraw = 1, left = (winsize.ws_col - ((int)sizeof("> Single player") - 1)) / 2; + char board[3][3], marks[3][3], winner, player; + const char *highlight; + int x, y, empty, rand_move = 0, confline = 0; + + ARGBEGIN { + default: + usage(); + break; + } ARGEND; + if (argc) + usage(); + + memset(&termctx, 0, sizeof(termctx)); + memset(&sigact, 0, sizeof(sigact)); + + sigact.sa_handler = sigterm; + sigaction(SIGTERM, &sigact, NULL); + sigaction(SIGQUIT, &sigact, NULL); + sigaction(SIGINT, &sigact, NULL); + + sigact.sa_handler = sigwinch; + sigaction(SIGWINCH, &sigact, NULL); + + if (tcgetattr(STDIN_FILENO, &stty)) + eprintf("tcgetattr <stdin>:"); + saved_stty = stty; + stty.c_lflag &= (tcflag_t)~(ECHO | ICANON); + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &stty)) + eprintf("tcsetattr <stdin> TCSAFLUSH:"); + + printf("\033[?1049;1003;1006h\033[?25l"); + + libsimple_eprintf_preprint = restore_terminal; + +restart: + for (;;) { + if (caught_sigterm) + goto out; + if (caught_sigwinch) { + if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { + if (errno == EINTR) + continue; + eprintf("ioctl <stdout> TIOCGWINSZ:"); + } + caught_sigwinch = 0; + left = (winsize.ws_col - ((int)sizeof("> Single player") - 1)) / 2; + redraw = 1; + } + + if (redraw) { + printf("\033[H\033[m"); + for (r = (winsize.ws_row - 2) / 2; r > 0; r--) + printf("\033[K\n"); + printf("%*.s%s %s\033[m\033[K\n", left, "", dualplayer == 0 ? "\033[1;31m>" : " ", "Single player"); + printf("%*.s%s %s\033[m\033[J", left, "", dualplayer == 1 ? "\033[1;31m>" : " ", "Dual player"); + fflush(stdout); + redraw = 0; + } + + r = libterminput_read(STDIN_FILENO, &input, &termctx); + if (r <= 0) { + if (r < 0) { + if (errno == EINTR) + continue; + libsimple_eprintf("libterminput_read <stdin>:"); + } + break; + } + + if (input.type == LIBTERMINPUT_KEYPRESS) { + if (input.keypress.key == LIBTERMINPUT_UP && dualplayer > 0) { + dualplayer -= 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_DOWN && dualplayer < 1) { + dualplayer += 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_ENTER) { + break; + } else if (input.keypress.key == LIBTERMINPUT_SYMBOL) { + if (!strcmp(input.keypress.symbol, " ")) { + break; + } else if (!strcmp(input.keypress.symbol, "q") || !strcmp(input.keypress.symbol, "Q")) { + goto out; + } else if (!strcmp(input.keypress.symbol, "L") && (input.keypress.mods & LIBTERMINPUT_CTRL)) { + redraw = 1; + } + } + } else if (input.type == LIBTERMINPUT_MOUSEEVENT && + input.mouseevent.event == LIBTERMINPUT_PRESS && + input.mouseevent.button == LIBTERMINPUT_BUTTON1 && + input.mouseevent.x > (left < 0 ? 0 : (size_t)left) && + input.mouseevent.x - (left < 0 ? 0 : (size_t)left) <= (int)sizeof("> Single player") - 1) { + input.mouseevent.y -= (size_t)((winsize.ws_row < 2 ? 2 : winsize.ws_row) - 2) / 2; + switch (input.mouseevent.y) { + case 1: + dualplayer = 0; + goto new_game; + case 2: + dualplayer = 1; + goto new_game; + default: + break; + } + } + } + +new_game: + redraw = 1; + x = y = 1; + player = 'X'; + empty = 9; + winner = ' '; + highlight = "\033[1;31m"; + memset(board, ' ', sizeof(board)); + + if (!dualplayer) { + left = (winsize.ws_col - ((int)sizeof("> [X] CPU's first move is random") - 1)) / 2; + for (;;) { + if (caught_sigterm) + goto out; + if (caught_sigwinch) { + if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { + if (errno == EINTR) + continue; + eprintf("ioctl <stdout> TIOCGWINSZ:"); + } + caught_sigwinch = 0; + left = (winsize.ws_col - ((int)sizeof("> [X] CPU's first move is random") - 1)) / 2; + redraw = 1; + } + + if (redraw) { + printf("\033[H\033[m"); + for (r = (winsize.ws_row - 5) / 2; r > 0; r--) + printf("\033[K\n"); + printf("%*.s%s [%s] CPU begins\033[m\033[K\n", left, "", + confline == 0 ? "\033[1;31m>" : " ", cpu_begins ? "X" : " "); + printf("%*.s%s [%s] CPU's first move is random\033[m\033[K\n", left, "", + confline == 1 ? "\033[1;31m>" : " ", rand_on_first ? "X" : " "); + printf("%*.s%s Difficulty: %i\033[m\033[K\n", left, "", + confline == 2 ? "\033[1;31m>" : " ", level); + printf("%*.s%s [PLAY]\033[m\033[K\n", left, "", + confline == 3 ? "\033[1;31m>" : " "); + printf("%*.s%s [back]\033[m\033[J", left, "", + confline == 4 ? "\033[1;31m>" : " "); + fflush(stdout); + redraw = 0; + } + + r = libterminput_read(STDIN_FILENO, &input, &termctx); + if (r <= 0) { + if (r < 0) { + if (errno == EINTR) + continue; + libsimple_eprintf("libterminput_read <stdin>:"); + } + break; + } + + if (input.type == LIBTERMINPUT_KEYPRESS) { + if (input.keypress.key == LIBTERMINPUT_UP && confline > 0) { + confline -= 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_DOWN && confline < 4) { + confline += 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_LEFT && confline == 2 && level > 0) { + level -= 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_RIGHT && confline == 2 && level < 9) { + level += 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_ENTER) { + trigger_conf: + if (confline == 0) { + cpu_begins ^= 1; + redraw = 1; + } else if (confline == 1) { + rand_on_first ^= 1; + redraw = 1; + } else if (confline == 3) { + break; + } else if (confline == 4) { + redraw = 1; + goto restart; + } + } else if (input.keypress.key == LIBTERMINPUT_SYMBOL) { + if (!strcmp(input.keypress.symbol, " ")) { + goto trigger_conf; + } else if (!strcmp(input.keypress.symbol, "q") || !strcmp(input.keypress.symbol, "Q")) { + goto out; + } else if (!strcmp(input.keypress.symbol, "L") && (input.keypress.mods & LIBTERMINPUT_CTRL)) { + redraw = 1; + } + } + } else if (input.type == LIBTERMINPUT_MOUSEEVENT && + input.mouseevent.event == LIBTERMINPUT_PRESS && + input.mouseevent.x > (left < 0 ? 0 : (size_t)left)) { + input.mouseevent.x -= (left < 0 ? 0 : (size_t)left); + input.mouseevent.y -= (size_t)((winsize.ws_row < 5 ? 5 : winsize.ws_row) - 5) / 2; + if (input.mouseevent.y > 0 && input.mouseevent.y <= 5 && + input.mouseevent.x <= (int)sizeof("> [X] CPU's first move is random") - 1) { + if (input.mouseevent.button == LIBTERMINPUT_BUTTON1 || + ((int)input.mouseevent.y - 1 == 2 && + (input.mouseevent.button == LIBTERMINPUT_BUTTON3 || + input.mouseevent.button == LIBTERMINPUT_XBUTTON1|| + input.mouseevent.button == LIBTERMINPUT_XBUTTON2))) { + confline = (int)input.mouseevent.y - 1; + if (confline != 2) + goto trigger_conf; + if (input.mouseevent.button == LIBTERMINPUT_BUTTON1 || + input.mouseevent.button == LIBTERMINPUT_XBUTTON2) { + level += 1; + level %= 10; + } else if (level) { + level -= 1; + } else { + level = 9; + } + redraw = 1; + } + + } + } + } + left = (winsize.ws_col - ((int)sizeof("| X | X | X |") - 1)) / 2; + redraw = 1; + rand_move = rand_on_first; + if (cpu_begins) { + player = 'O'; + highlight = "\033[1;34m"; + goto cpu_move; + } + } + + left = (winsize.ws_col - ((int)sizeof("| X | X | X |") - 1)) / 2; + + for (;;) { + if (caught_sigterm) + goto out; + if (caught_sigwinch) { + if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { + if (errno == EINTR) + continue; + eprintf("ioctl <stdout> TIOCGWINSZ:"); + } + caught_sigwinch = 0; + left = (winsize.ws_col - ((int)sizeof("| X | X | X |") - 1)) / 2; + redraw = 1; + } + + if (redraw) { + printf("\033[H\033[m"); + for (r = (winsize.ws_row - 8) / 2; r > 0; r--) + printf("\033[K\n"); + printf("%*.s%s┌───%s┬%s───%s┬%s───┐%s\033[K\n", left, "", + (y == 0 && x == 0) ? highlight : "", + (y == 0 && x == 1) ? highlight : "", + (y == 0 && x == 0) ? "\033[0m" : "", + (y == 0 && x == 2) ? highlight : "", + (y == 0 && x == 1) ? "\033[0m" : "", + (y == 0 && x == 2) ? "\033[0m" : ""); + printf("%*.s%s│ %c %s│%s %c %s│%s %c │%s\033[K\n", left, "", + (y == 0 && x == 0) ? highlight : "", + (y == 0 && x == 0 && board[0][0] == ' ') ? '+' : board[0][0], + (y == 0 && x == 1) ? highlight : "", + (y == 0 && x == 0) ? "\033[0m" : "", + (y == 0 && x == 1 && board[0][1] == ' ') ? '+' : board[0][1], + (y == 0 && x == 2) ? highlight : "", + (y == 0 && x == 1) ? "\033[0m" : "", + (y == 0 && x == 2 && board[0][2] == ' ') ? '+' : board[0][2], + (y == 0 && x == 2) ? "\033[0m" : ""); + printf("%*.s%s├───%s┼%s───%s┼%s───┤%s\033[K\n", left, "", + (y <= 1 && x == 0) ? highlight : "", + (y <= 1 && x == 1) ? highlight : "", + (y <= 1 && x == 0) ? "\033[0m" : "", + (y <= 1 && x == 2) ? highlight : "", + (y <= 1 && x == 1) ? "\033[0m" : "", + (y <= 1 && x == 2) ? "\033[0m" : ""); + printf("%*.s%s│ %c %s│%s %c %s│%s %c │%s\033[K\n", left, "", + (y == 1 && x == 0) ? highlight : "", + (y == 1 && x == 0 && board[1][0] == ' ') ? '+' : board[1][0], + (y == 1 && x == 1) ? highlight : "", + (y == 1 && x == 0) ? "\033[0m" : "", + (y == 1 && x == 1 && board[1][1] == ' ') ? '+' : board[1][1], + (y == 1 && x == 2) ? highlight : "", + (y == 1 && x == 1) ? "\033[0m" : "", + (y == 1 && x == 2 && board[1][2] == ' ') ? '+' : board[1][2], + (y == 1 && x == 2) ? "\033[0m" : ""); + printf("%*.s%s├───%s┼%s───%s┼%s───┤%s\033[K\n", left, "", + (y >= 1 && x == 0) ? highlight : "", + (y >= 1 && x == 1) ? highlight : "", + (y >= 1 && x == 0) ? "\033[0m" : "", + (y >= 1 && x == 2) ? highlight : "", + (y >= 1 && x == 1) ? "\033[0m" : "", + (y >= 1 && x == 2) ? "\033[0m" : ""); + printf("%*.s%s│ %c %s│%s %c %s│%s %c │%s\033[K\n", left, "", + (y == 2 && x == 0) ? highlight : "", + (y == 2 && x == 0 && board[2][0] == ' ') ? '+' : board[2][0], + (y == 2 && x == 1) ? highlight : "", + (y == 2 && x == 0) ? "\033[0m" : "", + (y == 2 && x == 1 && board[2][1] == ' ') ? '+' : board[2][1], + (y == 2 && x == 2) ? highlight : "", + (y == 2 && x == 1) ? "\033[0m" : "", + (y == 2 && x == 2 && board[2][2] == ' ') ? '+' : board[2][2], + (y == 2 && x == 2) ? "\033[0m" : ""); + printf("%*.s%s└───%s┴%s───%s┴%s───┘%s\033[J", left, "", + (y == 2 && x == 0) ? highlight : "", + (y == 2 && x == 1) ? highlight : "", + (y == 2 && x == 0) ? "\033[0m" : "", + (y == 2 && x == 2) ? highlight : "", + (y == 2 && x == 1) ? "\033[0m" : "", + (y == 2 && x == 2) ? "\033[0m" : ""); + fflush(stdout); + redraw = 0; + } + + r = libterminput_read(STDIN_FILENO, &input, &termctx); + if (r <= 0) { + if (r < 0) { + if (errno == EINTR) + continue; + libsimple_eprintf("libterminput_read <stdin>:"); + } + break; + } + + if (input.type == LIBTERMINPUT_KEYPRESS) { + if (input.keypress.key == LIBTERMINPUT_UP && y > 0) { + y -= 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_DOWN && y < 2) { + y += 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_LEFT && x > 0) { + x -= 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_RIGHT && x < 2) { + x += 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_ENTER) { + select: + if (board[y][x] == ' ') { + board[y][x] = player; + redraw = 1; + get_winner(board, &winner); + empty -= 1; + if (!empty || winner != ' ') + break; + if (!dualplayer) { + cpu_move: + cpu_play(board, player ^ 'X' ^ 'O', empty, rand_move); + rand_move = 0; + get_winner(board, &winner); + empty -= 1; + if (!empty || winner != ' ') + break; + } else { + player ^= 'X' ^ 'O'; + highlight = player == 'X' ? "\033[1;31m" : "\033[1;34m"; + } + } + } else if (input.keypress.key == LIBTERMINPUT_SYMBOL) { + if (!strcmp(input.keypress.symbol, " ")) { + goto select; + } else if (!strcmp(input.keypress.symbol, "q") || !strcmp(input.keypress.symbol, "Q")) { + goto out; + } else if (!strcmp(input.keypress.symbol, "L") && (input.keypress.mods & LIBTERMINPUT_CTRL)) { + redraw = 1; + } else if (!strcmp(input.keypress.symbol, "7")) { + y = 0, x = 0; + goto select; + } else if (!strcmp(input.keypress.symbol, "8")) { + y = 0, x = 1; + goto select; + } else if (!strcmp(input.keypress.symbol, "9")) { + y = 0, x = 2; + goto select; + } else if (!strcmp(input.keypress.symbol, "4")) { + y = 1, x = 0; + goto select; + } else if (!strcmp(input.keypress.symbol, "5")) { + y = 1, x = 1; + goto select; + } else if (!strcmp(input.keypress.symbol, "6")) { + y = 1, x = 2; + goto select; + } else if (!strcmp(input.keypress.symbol, "1")) { + y = 2, x = 0; + goto select; + } else if (!strcmp(input.keypress.symbol, "2")) { + y = 2, x = 1; + goto select; + } else if (!strcmp(input.keypress.symbol, "3")) { + y = 2, x = 2; + goto select; + } + } + } else if (input.type == LIBTERMINPUT_MOUSEEVENT && + input.mouseevent.event == LIBTERMINPUT_PRESS && + input.mouseevent.button == LIBTERMINPUT_BUTTON1) { + input.mouseevent.x -= (left + 2) < 1 ? 1 : (size_t)(left + 2); + input.mouseevent.y -= (size_t)(((winsize.ws_row < 8 ? 8 : winsize.ws_row) - 8) / 2 + 1); + if (input.mouseevent.x < 4 * 3 && input.mouseevent.y < 7 && + (input.mouseevent.x & 3) != 3 && (input.mouseevent.y & 1)) { + y = (int)input.mouseevent.y / 2; + x = (int)input.mouseevent.x / 4; + goto select; + } + } + } + + get_win_combination(board, marks); + if (winner == 'X') + highlight = "\033[1;31m"; + else + highlight = "\033[1;34m"; + + for (;;) { + if (caught_sigterm) + goto out; + if (caught_sigwinch) { + if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { + if (errno == EINTR) + continue; + eprintf("ioctl <stdout> TIOCGWINSZ:"); + } + caught_sigwinch = 0; + left = (winsize.ws_col - ((int)sizeof("| X | X | X |") - 1)) / 2; + redraw = 1; + } + + if (redraw) { + printf("\033[H\033[m"); + for (r = (winsize.ws_row - 8) / 2; r > 0; r--) + printf("\033[K\n"); + printf("%*.s┌───┬───┬───┐\033[K\n", left, ""); + printf("%*.s│ %s%c%s │ %s%c%s │ %s%c%s │\033[K\n", left, "", + marks[0][0] ? highlight : "", board[0][0], "\033[m", + marks[0][1] ? highlight : "", board[0][1], "\033[m", + marks[0][2] ? highlight : "", board[0][2], "\033[m"); + printf("%*.s├───┼───┼───┤\033[K\n", left, ""); + printf("%*.s│ %s%c%s │ %s%c%s │ %s%c%s │\033[K\n", left, "", + marks[1][0] ? highlight : "", board[1][0], "\033[m", + marks[1][1] ? highlight : "", board[1][1], "\033[m", + marks[1][2] ? highlight : "", board[1][2], "\033[m"); + printf("%*.s├───┼───┼───┤\033[K\n", left, ""); + printf("%*.s│ %s%c%s │ %s%c%s │ %s%c%s │\033[K\n", left, "", + marks[2][0] ? highlight : "", board[2][0], "\033[m", + marks[2][1] ? highlight : "", board[2][1], "\033[m", + marks[2][2] ? highlight : "", board[2][2], "\033[m"); + printf("%*.s└───┴───┴───┘\033[K\n", left, ""); + if (winner == ' ') + printf("%*.s\033[1m Draw!\033[J", left, ""); + else + printf("%*.s\033[1;%sm %c won!!\033[J", left, "", winner == 'X' ? "31" : "34", winner); + fflush(stdout); + redraw = 0; + redraw = 0; + } + + r = libterminput_read(STDIN_FILENO, &input, &termctx); + if (r <= 0) { + if (r < 0) { + if (errno == EINTR) + continue; + libsimple_eprintf("libterminput_read <stdin>:"); + } + break; + } + + if (input.type == LIBTERMINPUT_KEYPRESS) { + if (input.keypress.key == LIBTERMINPUT_ENTER) { + goto new_game; + } else if (input.keypress.key == LIBTERMINPUT_SYMBOL) { + if (!strcmp(input.keypress.symbol, " ")) + goto new_game; + else if (!strcmp(input.keypress.symbol, "q") || !strcmp(input.keypress.symbol, "Q")) + goto out; + else if (!strcmp(input.keypress.symbol, "L") && (input.keypress.mods & LIBTERMINPUT_CTRL)) + redraw = 1; + } + } else if (input.type == LIBTERMINPUT_MOUSEEVENT && + input.mouseevent.event == LIBTERMINPUT_PRESS && + input.mouseevent.button == LIBTERMINPUT_BUTTON1) { + goto new_game; + } + } + +out: + restore_terminal(); + return 0; +} |
