diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/user/ttt/main.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/src/user/ttt/main.c b/src/user/ttt/main.c new file mode 100644 index 0000000..655bf8e --- /dev/null +++ b/src/user/ttt/main.c @@ -0,0 +1,271 @@ +#include <pland/syscall.h> +#include <popups/info.h> +#include <knob/rand.h> + +#define SPACE_SIZE 20 +#define BORDER_SIZE 4 +#define WIDTH (SPACE_SIZE * 3 + BORDER_SIZE * 4) + +#define BG ((_pixel_t){.r = 0xd6, .g = 0xd6, .b = 0xd6}) +#define FG ((_pixel_t){.r = 0x33, .g = 0x33, .b = 0x33}) +#define XC ((_pixel_t){.r = 0x60, .g = 0x06, .b = 0x43}) +#define OC ((_pixel_t){.r = 0x2c, .g = 0x77, .b = 0x30}) +#define SL ((_pixel_t){.r = 0x9d, .g = 0x99, .b = 0xc2}) + +static const bool x_pic[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0, + 0,0,0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,0,0, + 0,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0, + 0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0, + 0,0,0,0,0,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0, + 0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0, + 0,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0, + 0,0,0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,0,0, + 0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +static const bool o_pic[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0, + 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0, + 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0, + 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +static const bool b_pic[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +typedef enum {B, X, O} trit; + +static const bool *pics[] = {b_pic, x_pic, o_pic}; +static const _pixel_t colors[] = {FG, XC, OC}; + +static _window_handle_t w; +static _pixel_t pbuf[WIDTH * WIDTH]; +static trit board_state[9]; +static uint8_t cursor; +static trit playing_as; +static uint8_t filled_squares; + +__attribute__ ((__pure__)) +static bool is_win(uint8_t hint) { + const uint8_t c = hint % 3; + if ((board_state[c] == board_state[c + 3]) && (board_state[c] == board_state[c + 6])) + return true; + const uint8_t r = hint - c; + if ((board_state[r] == board_state[r + 1]) && (board_state[r] == board_state[r + 2])) + return true; + return board_state[4] && ( + ((board_state[0] == board_state[4]) && (board_state[0] == board_state[8])) || + ((board_state[2] == board_state[4]) && (board_state[2] == board_state[6])) + ); +} + +static void draw_rect(uint8_t y, uint8_t x, uint8_t h, uint8_t w, _pixel_t color) { + for (uint8_t yy = y; yy < y + h; ++yy) + for (uint8_t xx = x; xx < x + w; ++xx) + pbuf[yy * WIDTH + xx] = color; +} + +static void draw_spot(uint8_t n) { + const _pixel_t bg_c = n == cursor ? SL : BG; + const _pixel_t fg_c = colors[board_state[n]]; + const bool *const pic = pics[board_state[n]]; + + const uint8_t ys = BORDER_SIZE + (n / 3) * (BORDER_SIZE + SPACE_SIZE); + const uint8_t xs = BORDER_SIZE + (n % 3) * (BORDER_SIZE + SPACE_SIZE); + for (uint8_t y = 0; y < SPACE_SIZE; ++y) + for (uint8_t x = 0; x < SPACE_SIZE; ++x) + pbuf[(ys + y) * WIDTH + xs + x] = pic[y * SPACE_SIZE + x] ? fg_c : bg_c; +} + +static void ai_turn(); + +static void reset_board() { + cursor = 4; + playing_as = gen_rand() % 2 + 1; + filled_squares = 0; + for (uint8_t i = 0; i < 9; ++i) { + board_state[i] = B; + draw_spot(i); + } + + if (gen_rand() % 2) + ai_turn(); +} + +static uint32_t start_time; + +static void on_win() { + struct popup p; + info_popupf(&p, "You win! Time: %us\nPress escape to play again.", FG, BG, _get_timestamp() - start_time); + make_modal(&p); + reset_board(); + _paint_window(w); + start_time = _get_timestamp(); +} + +static void on_tie() { + struct popup p; + info_popupf(&p, "Tie! Time: %us\nPress escape to play again.", FG, BG, _get_timestamp() - start_time); + make_modal(&p); + reset_board(); + _paint_window(w); + start_time = _get_timestamp(); +} + +static void ai_turn() { + //super easy mode + uint8_t n; + do + n = gen_rand() % 9; + while (board_state[n]); + board_state[n] = playing_as == O ? X : O; + draw_spot(n); + _paint_window(w); + if (is_win(n)) { + struct popup p; + info_popupf(&p, "You lose. Time: %us\nPress escape to play again.", FG, BG, _get_timestamp() - start_time); + make_modal(&p); + reset_board(); + _paint_window(w); + start_time = _get_timestamp(); + } + else if (++filled_squares == 9) + on_tie(); +} + +void main() { + struct popup controls; + info_popup(&controls, + "Tic-Tac-Toe Controls:\n\n" + "Arrow keys: move selection\n" + "Spacebar: confirm selection\n" + "Escape: quit\n\n" + "Press escape to start.", FG, BG); + make_modal(&controls); + + for (uint16_t i = 0; i < WIDTH * WIDTH; ++i) + pbuf[i] = BG; + + draw_rect(SPACE_SIZE + BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, WIDTH - 2 * BORDER_SIZE, FG); + draw_rect(BORDER_SIZE, SPACE_SIZE + BORDER_SIZE, WIDTH - 2 * BORDER_SIZE, BORDER_SIZE, FG); + draw_rect(SPACE_SIZE * 2 + BORDER_SIZE * 2, BORDER_SIZE, BORDER_SIZE, WIDTH - 2 * BORDER_SIZE, FG); + draw_rect(BORDER_SIZE, SPACE_SIZE * 2 + BORDER_SIZE * 2, WIDTH - 2 * BORDER_SIZE, BORDER_SIZE, FG); + + reset_board(); + + w = _new_window(WIDTH, WIDTH, pbuf); + start_time = _get_timestamp(); + + while (1) { + struct window_action wa; + _get_win_action(w, &wa); + if (!wa.action_type) { + _wait_for_action(); + _yield_task(); + continue; + } + + if (wa.action_type != KEY_DOWN) + continue; + + switch (wa.as_key.key_id) { + case KEY_UP_ARROW: + if (cursor >= 3) { + cursor -= 3; + draw_spot(cursor + 3); + draw_spot(cursor); + _paint_window(w); + } + break; + case KEY_DOWN_ARROW: + if (cursor < 6) { + cursor += 3; + draw_spot(cursor - 3); + draw_spot(cursor); + _paint_window(w); + } + break; + case KEY_LEFT_ARROW: + if (cursor % 3) { + --cursor; + draw_spot(cursor + 1); + draw_spot(cursor); + _paint_window(w); + } + break; + case KEY_RIGHT_ARROW: + if (cursor % 3 != 2) { + ++cursor; + draw_spot(cursor - 1); + draw_spot(cursor); + _paint_window(w); + } + break; + case KEY_ESCAPE: + return; + case KEY_SPACE: + if (!board_state[cursor]) { + board_state[cursor] = playing_as; + draw_spot(cursor); + _paint_window(w); + if (is_win(cursor)) + on_win(); + else if (++filled_squares == 9) + on_tie(); + else + ai_turn(); + } + break; + default: + break; + } + } +}
\ No newline at end of file |