From 7d7487fd43f1c4c2f3fdd671f0cd6c3f471892b1 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Thu, 19 Feb 2026 17:51:57 +0100 Subject: Add rock-paper-scissor (WIP) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- rock-paper-scissors.c | 369 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 rock-paper-scissors.c (limited to 'rock-paper-scissors.c') diff --git a/rock-paper-scissors.c b/rock-paper-scissors.c new file mode 100644 index 0000000..c18cdde --- /dev/null +++ b/rock-paper-scissors.c @@ -0,0 +1,369 @@ +/* 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 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); +} + + +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; + const char *highlight; + int confline = 0, line = 0, player = 0, level = 0; + uintmax_t wins, loses; + + 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 :"); + saved_stty = stty; + stty.c_lflag &= (tcflag_t)~(ECHO | ICANON); + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &stty)) + eprintf("tcsetattr 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 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 :"); + } + 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; + player = 0; + highlight = "\033[1;31m"; + + if (!dualplayer) { + left = (winsize.ws_col - ((int)sizeof("> [PLAY] Select mode randomly") - 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 TIOCGWINSZ:"); + } + caught_sigwinch = 0; + left = (winsize.ws_col - ((int)sizeof("> [PLAY] Select mode randomly") - 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 Pattern depth: %i\033[m\033[K\n", left, "", + confline == 0 ? "\033[1;31m>" : " ", level); + printf("%*.s%s [PLAY] Select mode randomly\033[m\033[K\n", left, "", + confline == 1 ? "\033[1;31m>" : " "); + printf("%*.s%s [PLAY] Offensive mode\033[m\033[K\n", left, "", + confline == 2 ? "\033[1;31m>" : " "); + printf("%*.s%s [PLAY] Defensive mode\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 :"); + } + 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 == 0 && level > 0) { + level -= 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_RIGHT && confline == 0 && level < 101) { + level += 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_ENTER) { + trigger_conf: + if (confline == 1) { + /* TODO select mode randomly */ + break; + } else if (confline == 2) { + /* TODO offensive mode */ + break; + } else if (confline == 3) { + /* TODO defensive mode */ + 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 != 0) + goto trigger_conf; + if (input.mouseevent.button == LIBTERMINPUT_BUTTON1 || + input.mouseevent.button == LIBTERMINPUT_XBUTTON2) { + level += 1; + level %= 101; + } else if (level) { + level -= 1; + } else { + level = 100; + } + redraw = 1; + } + + } + } + } + redraw = 1; + } + + left = (winsize.ws_col - ((int)sizeof("> Scissors") - 1)) / 2; + wins = 0; + loses = 0; + + for (;;) { + if (caught_sigterm) + goto out; + if (caught_sigwinch) { + if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { + if (errno == EINTR) + continue; + eprintf("ioctl TIOCGWINSZ:"); + } + caught_sigwinch = 0; + left = (winsize.ws_col - ((int)sizeof("> Scissors") - 1)) / 2; + redraw = 1; + } + + if (redraw) { + printf("\033[H\033[m"); + for (r = (winsize.ws_row - 7) / 2; r > 0; r--) + printf("\033[K\n"); + if (!dualplayer) { + printf("%*.s\033[1;31m%ju (wins)\033[0m : \033[1;34m%ju (loses)\033[0m\033[K\n", + (winsize.ws_row - 3) / 2 - snprintf(NULL, 0, "%ju (wins)", wins), + "", wins, loses); + } else { + printf("%*.s\033[1;31m%ju (P1)\033[0m : \033[1;34m%ju (P2)\033[0m\033[K\n", + (winsize.ws_row - 3) / 2 - snprintf(NULL, 0, "%ju (P1)", wins), + "", wins, loses); + } + printf("%*.s%s%s%s\033[K\n\n", left, "", + line == 0 ? highlight : "", "Rock", + line == 0 ? "\033[0m" : ""); + printf("%*.s%s%s%s\033[K\n\n", left, "", + line == 1 ? highlight : "", "Paper", + line == 1 ? "\033[0m" : ""); + printf("%*.s%s%s%s\033[J", left, "", + line == 2 ? highlight : "", "Scissors", + line == 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 :"); + } + break; + } + + if (input.type == LIBTERMINPUT_KEYPRESS) { + if (input.keypress.key == LIBTERMINPUT_UP && line > 0) { + line -= 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_DOWN && line < 2) { + line += 1; + redraw = 1; + } else if (input.keypress.key == LIBTERMINPUT_ENTER) { + select: + ;/* TODO */ + } 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, "r") || + !strcmp(input.keypress.symbol, "R") || + !strcmp(input.keypress.symbol, "1")) { + line = 0; + goto select; + } else if (!strcmp(input.keypress.symbol, "p") || + !strcmp(input.keypress.symbol, "P") || + !strcmp(input.keypress.symbol, "2")) { + line = 1; + goto select; + } else if (!strcmp(input.keypress.symbol, "s") || + !strcmp(input.keypress.symbol, "S") || + !strcmp(input.keypress.symbol, "3")) { + line = 2; + goto select; + } + } + } else if (input.type == LIBTERMINPUT_MOUSEEVENT && + input.mouseevent.event == LIBTERMINPUT_PRESS && + input.mouseevent.button == LIBTERMINPUT_BUTTON1) { + /* TODO */ + } + } + +out: + restore_terminal(); + return 0; +} -- cgit v1.2.3-70-g09d2