#define _POSIX_C_SOURCE 202511L #include #include #include #include "wrmr.h" #include "vui.h" /* input buffer */ int inbuf[32] = { 0 }; #define INBUF_MASK 0x1f u32 inbuf_start = 0, inbuf_end = 0; int pop_input(void) { /* int k = inbuf[inbuf_start]; inbuf[inbuf_start] = 0; if (k) inbuf_start = (inbuf_start + 1) & INBUF_MASK; return k; */ if (inbuf_start == inbuf_end) return 0; int k = inbuf[inbuf_start++]; inbuf_start &= INBUF_MASK; return k; } void push_input(int i) { inbuf[inbuf_end++] = i; inbuf_end &= INBUF_MASK; } u32 poll_input_iter = 0; int poll_input(u32 ms) { struct timespec start; clock_gettime(CLOCK_MONOTONIC, &start); u64 n = start.tv_nsec + (ms * 1000000); struct timespec stop = { .tv_sec = start.tv_sec + n / 1000000000, .tv_nsec = n % 1000000000 }; poll_input_iter = 0; for (;;) { poll_input_iter++; struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); i64 ns_rem = (stop.tv_sec - now.tv_sec) * 1000000000 + (i64)stop.tv_nsec - (i64)now.tv_nsec; i64 ms_rem = ns_rem / 1000000; if (ns_rem < 1) break; if (vui_wait_for_input(ms_rem)) push_input(vui_key()); else break; } return 0; } /* program */ #define WIDTH 32 #define HEIGHT 32 typedef enum { TILE_FLOOR, TILE_SNK_ODD, TILE_SNK_EVEN, TILE_APPLE, TILE_MAX } Tile; const VuiAttr tile_attr[TILE_MAX][2] = { { FG_BLUE | BG_BLACK, FG_BLUE | BG_BLACK }, { FG_YELLOW | BG_BLACK, FG_BYELLOW | BG_BLACK }, { FG_GREEN | BG_BLACK, FG_BGREEN | BG_BLACK }, { FG_BGREEN | BG_BLACK, FG_BRED | BG_BLACK }, }; const VuiChar tile_chr[TILE_MAX][2] = { { ' ', '.' }, { '[', ']' }, { '[', ']' }, { '`', 'o' } }; Tile board[HEIGHT][WIDTH]; typedef enum { DIR_LEFT, DIR_UP, DIR_RIGHT, DIR_DOWN } Dir; #define DIR_IS_Y(d) ((d)&1) #define DIR_IS_X(d) (~(d)&1) #define DIR_SIGN(d) ((d)&2 ? 1 : -1) #define DIR_FLIP(d) (((d) + 2) & 3) #define DIR_DX(d) (DIR_IS_X(d) * DIR_SIGN(d)) #define DIR_DY(d) (DIR_IS_Y(d) * DIR_SIGN(d)) typedef struct { i16 x, y; } SnkSeg; #define SNK_SEG_MAX (WIDTH * HEIGHT) typedef struct { Dir dir; u32 len; SnkSeg seg[SNK_SEG_MAX]; } Snake; Snake snk = { 0 }; typedef struct { i16 x, y; } Apple; #define APPLE_MAX 32 Apple apple[APPLE_MAX]; void replace_apple(u32 i) { Apple *a = &apple[i]; do { a->x = rand() % WIDTH; a->y = rand() % HEIGHT; } while (board[a->y][a->x] != TILE_FLOOR); } void place_apple(void) { for (u32 i = 0; i < APPLE_MAX; i++) { replace_apple(i); } } void brd_clear(void) { for (u32 y = 0; y < HEIGHT; y++) { for (u32 x = 0; x < WIDTH; x++) { board[y][x] = TILE_FLOOR; } } } void brd_draw(void) { /* u32 left = COLS / 2 - WIDTH; u32 top = LINES / 2 - HEIGHT / 2; */ u32 left = COLS / 2 - snk.seg->x * 2; u32 top = LINES / 2 - snk.seg->y; u32 right = left + WIDTH * 2; u32 bottom = top + HEIGHT; for (u32 y = 0; y < HEIGHT; y++) { vui_chr(left - 1, top + y, u'║'); vui_chr(right + 1, top + y, u'║'); for (u32 x = 0; x < WIDTH; x++) { Tile t = board[y][x]; vui_chra(left + 2 * x, top + y, tile_chr[t][0], tile_attr[t][0]); vui_chra(left + 2 * x + 1, top + y, tile_chr[t][1], tile_attr[t][1]); vui_puts(left + 2 * x, top - 1, "══"); vui_puts(left + 2 * x, bottom, "══"); } } vui_chr(left - 1, top - 1, u'╔'); vui_chr(right + 1, top - 1, u'╗'); vui_chr(left - 1, bottom, u'╚'); vui_chr(right + 1, bottom, u'╝'); vui_chr(right, bottom, u'═'); vui_chr(right, top - 1, u'═'); /* u32 x = 0; u32 y = 1; for (u32 i = 0; i < sizeof inbuf / sizeof *inbuf; i++) { int within = (i >= inbuf_start && i < inbuf_end); if (inbuf_end < inbuf_start) { within = i >= inbuf_start || i < inbuf_end; } VuiAttr a = within ? FG_BCYAN : FG_BBLACK; if (i == inbuf_start) vui_chra(x, bottom + y - 1, 'v', FG_BCYAN); if (i == inbuf_end) vui_chra(x + 1, bottom + y - 1, 'v', FG_BMAGENTA); x += vui_aprintf(x, bottom + y, a, "%6X ", inbuf[i]); if (x >= COLS - 24) { x = 0; y += 2; } } */ } void snk_draw(Snake *s) { for (u32 i = 0; i < s->len; i++) { board[s->seg[i].y][s->seg[i].x] = (i / 4) & 1 ? TILE_SNK_ODD : TILE_SNK_EVEN; } } void apple_draw(void) { for (u32 i = 0; i < APPLE_MAX; i++) { board[apple[i].y][apple[i].x] = TILE_APPLE; } } int alive, paused; void draw(void *ctx) { (void)ctx; vui_clear(); brd_clear(); snk_draw(&snk); apple_draw(); brd_draw(); if (!alive) { vui_puts(COLS/2 - 4, LINES/2, "YOU DIED!"); } vui_printf(0, 0, "input polling iterations: %u", poll_input_iter); } void update(void) { for (u32 i = 0; i < APPLE_MAX; i++) { if (snk.seg[0].x == apple[i].x && snk.seg[0].y == apple[i].y) { replace_apple(i); snk.len++; break; } } memmove(snk.seg + 1, snk.seg, (SNK_SEG_MAX - 1) * sizeof(SnkSeg)); i16 x = snk.seg[0].x + DIR_DX(snk.dir); i16 y = snk.seg[0].y + DIR_DY(snk.dir); if (x < 0 || y < 0 || x >= WIDTH || y >= HEIGHT) { alive = 0; return; } for (u32 i = 1; i < snk.len; i++) { if (snk.seg[i].x == x && snk.seg[i].y == y) { alive = 0; return; } } snk.seg[0].x = x; snk.seg[0].y = y; vui_scroll(-DIR_DX(snk.dir) * 2, -DIR_DY(snk.dir)); } void snk_chg_dir(Snake *s, Dir d) { if (s->len < 2 || d != DIR_FLIP(s->dir)) { s->dir = d; } } /* game */ int main(void) { srand(time(NULL)); vui_init(); snk.dir = DIR_RIGHT; snk.seg[snk.len++] = (SnkSeg) { 0, 0 }; alive = 1; paused = 0; vui_redraw_fn(draw); place_apple(); while (alive) { poll_input(30); draw(NULL); vui_blit(); switch (pop_input()) { case 0: break; case KEY_ESC: goto done; case KEY_LEFT : case 'j': snk_chg_dir(&snk, DIR_LEFT); break; case KEY_UP : case 'i': snk_chg_dir(&snk, DIR_UP); break; case KEY_RIGHT: case 'l': snk_chg_dir(&snk, DIR_RIGHT); break; case KEY_DOWN : case 'k': snk_chg_dir(&snk, DIR_DOWN); break; case ' ': paused = !paused; break; } if (!paused) update(); } draw(NULL); vui_blit(); (void)vui_key(); done: vui_fini(); return 0; }