summaryrefslogtreecommitdiff
path: root/src/user/ttt/main.c
diff options
context:
space:
mode:
authorBenji Dial <benji6283@gmail.com>2021-02-18 16:15:13 -0500
committerBenji Dial <benji6283@gmail.com>2021-02-18 16:15:13 -0500
commit82d74f17f40abfd30030da78606ceffedf4a9c20 (patch)
treef2710be3e9d876f5b63b18d0a83111182fd515d8 /src/user/ttt/main.c
parente427f85decc7949487228ec638993ae77fc1ca2c (diff)
downloadportland-os-82d74f17f40abfd30030da78606ceffedf4a9c20.tar.gz
(random ai) tic-tac-toe game
Diffstat (limited to 'src/user/ttt/main.c')
-rw-r--r--src/user/ttt/main.c271
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