diff --git a/overlay/rootfs/usr/bin/snake b/overlay/rootfs/usr/bin/snake index 5eda79d..7961641 100755 Binary files a/overlay/rootfs/usr/bin/snake and b/overlay/rootfs/usr/bin/snake differ diff --git a/overlay/rootfs/usr/bin/tetris b/overlay/rootfs/usr/bin/tetris index a1a0738..770f32c 100755 Binary files a/overlay/rootfs/usr/bin/tetris and b/overlay/rootfs/usr/bin/tetris differ diff --git a/prebuilt/games/usr/bin/snake b/prebuilt/games/usr/bin/snake index 5eda79d..7961641 100755 Binary files a/prebuilt/games/usr/bin/snake and b/prebuilt/games/usr/bin/snake differ diff --git a/prebuilt/games/usr/bin/tetris b/prebuilt/games/usr/bin/tetris index a1a0738..770f32c 100755 Binary files a/prebuilt/games/usr/bin/tetris and b/prebuilt/games/usr/bin/tetris differ diff --git a/src/games/snake.cpp b/src/games/snake.cpp index e2fe959..713e587 100644 --- a/src/games/snake.cpp +++ b/src/games/snake.cpp @@ -102,7 +102,8 @@ static State state = PLAYING; static int snake[COLS * ROWS][2]; // x,y per segment (0 = tail) static int snakelen = 4; static int dirx = 1, diry = 0; // current direction -static int ndirx = 1, ndiry = 0; // buffered next direction (latest keypress wins) +static int tqx[2], tqy[2]; // turn queue (max 2 pending turns) +static int tqn = 0; // number of queued turns static int foodx, foody; static int score = 0; static int best = 0; @@ -163,7 +164,7 @@ static void reset_game() { snake[i][1] = cy; } dirx = 1; diry = 0; - ndirx = 1; ndiry = 0; + tqn = 0; score = 0; pps = 150; update_speed(); @@ -172,10 +173,31 @@ static void reset_game() { nextTick = now_ms() + speed; } +// Queue a pending direction change; rapid key presses are stored so none are lost. +// Filters reversals and duplicates against the last queued (or current) direction. +static void tq_push(int dx, int dy) { + int refx = (tqn > 0) ? tqx[tqn - 1] : dirx; + int refy = (tqn > 0) ? tqy[tqn - 1] : diry; + if (dx == -refx && dy == -refy) return; // reversal + if (dx == refx && dy == refy) return; // duplicate + if (tqn < 2) { + tqx[tqn] = dx; + tqy[tqn] = dy; + tqn++; + } +} + static void step() { - // apply buffered direction (no reversing); latest keypress wins - if (!(ndirx == -dirx && ndiry == -diry) && !(ndirx == 0 && ndiry == 0)) { - dirx = ndirx; diry = ndiry; + // apply queued turn (no reversing); rapid presses are not lost + if (tqn > 0) { + int dx = tqx[0], dy = tqy[0]; + // shift queue + tqx[0] = tqx[1]; tqy[0] = tqy[1]; + tqn--; + // apply if not a reversal + if (!(dx == -dirx && dy == -diry) && !(dx == 0 && dy == 0)) { + dirx = dx; diry = dy; + } } int hx = snake[snakelen - 1][0] + dirx; int hy = snake[snakelen - 1][1] + diry; @@ -237,11 +259,21 @@ static void render() { XSetForeground(dpy, gc, C_FOOD); XFillRectangle(dpy, back, gc, gx0 + foodx * cell, gy0 + foody * cell, cell, cell); } - // snake - for (int i = 0; i < snakelen; i++) { - XSetForeground(dpy, gc, (i == snakelen - 1) ? C_HEAD : C_FG); - XFillRectangle(dpy, back, gc, gx0 + snake[i][0] * cell, gy0 + snake[i][1] * cell, cell, cell); + // snake (batched for speed) + XRectangle bodyRects[COLS * ROWS]; + int nbody = 0; + for (int i = 0; i < snakelen - 1; i++) { + bodyRects[nbody].x = gx0 + snake[i][0] * cell; + bodyRects[nbody].y = gy0 + snake[i][1] * cell; + bodyRects[nbody].width = cell; + bodyRects[nbody].height = cell; + nbody++; } + XSetForeground(dpy, gc, C_FG); + XFillRectangles(dpy, back, gc, bodyRects, nbody); + // head + XSetForeground(dpy, gc, C_HEAD); + XFillRectangle(dpy, back, gc, gx0 + snake[snakelen - 1][0] * cell, gy0 + snake[snakelen - 1][1] * cell, cell, cell); // score (top status strip, clear of the playfield border) char buf[32]; @@ -273,13 +305,13 @@ static void render() { static void handle_key(KeySym ks) { switch (ks) { case XK_Up: case XK_w: case XK_KP_Up: case XK_2: - ndirx = 0; ndiry = -1; break; + tq_push(0, -1); break; case XK_Down: case XK_s: case XK_KP_Down: case XK_8: case XK_x: - ndirx = 0; ndiry = 1; break; + tq_push(0, 1); break; case XK_Left: case XK_a: case XK_KP_Left: case XK_4: - ndirx = -1; ndiry = 0; break; + tq_push(-1, 0); break; case XK_Right: case XK_d: case XK_KP_Right: case XK_6: - ndirx = 1; ndiry = 0; break; + tq_push(1, 0); break; case XK_5: case XK_Return: case XK_KP_Enter: case XK_space: case XK_p: if (state == PLAYING) state = PAUSED; else if (state == PAUSED) { state = PLAYING; nextTick = now_ms() + speed; } diff --git a/src/games/tetris.cpp b/src/games/tetris.cpp index e517099..a7b7e1e 100644 --- a/src/games/tetris.cpp +++ b/src/games/tetris.cpp @@ -286,11 +286,32 @@ static void render() { // window edges, so it is visible on all four sides including right/bottom XSetForeground(dpy, gc, C_DIM); XDrawRectangle(dpy, back, gc, 2, 2, winW - 4, winH - 4); - // settled blocks + // settled blocks (batched for speed) + XRectangle bodies[PCOLS * PROWS], highlights[PCOLS * PROWS * 2], shadows[PCOLS * PROWS * 2]; + int nb = 0, nh = 0, ns = 0; for (int x = 0; x < PCOLS; x++) for (int y = 0; y < PROWS; y++) - if (board[x][y]) - draw_gb_cell(px0 + x * CELL, py0 + y * CELL, CELL, C_GB_MED); + if (board[x][y]) { + int bx = px0 + x * CELL, by = py0 + y * CELL; + bodies[nb].x = bx + 1; bodies[nb].y = by + 1; + bodies[nb].width = CELL - 2; bodies[nb].height = CELL - 2; + nb++; + highlights[nh].x = bx + 1; highlights[nh].y = by; + highlights[nh].width = CELL - 2; highlights[nh].height = 1; + nh++; + highlights[nh].x = bx; highlights[nh].y = by + 1; + highlights[nh].width = 1; highlights[nh].height = CELL - 2; + nh++; + shadows[ns].x = bx + 1; shadows[ns].y = by + CELL - 1; + shadows[ns].width = CELL - 2; shadows[ns].height = 1; + ns++; + shadows[ns].x = bx + CELL - 1; shadows[ns].y = by + 1; + shadows[ns].width = 1; shadows[ns].height = CELL - 2; + ns++; + } + XSetForeground(dpy, gc, C_GB_MED); XFillRectangles(dpy, back, gc, bodies, nb); + XSetForeground(dpy, gc, C_GB_PALE); XFillRectangles(dpy, back, gc, highlights, nh); + XSetForeground(dpy, gc, C_GB_DARK); XFillRectangles(dpy, back, gc, shadows, ns); // active piece if (state == PLAYING) {