From 1dd9fac3136a68e74d265b8ffb178248f42caa01 Mon Sep 17 00:00:00 2001 From: Luxferre Date: Fri, 23 Oct 2026 15:47:30 +0300 Subject: [PATCH] fix(games): correct Tetris rotation, fix input/render lag, visible window frame - Tetris: replace pivot-based rotation with explicit 4-state SHAPES table (classic SRS-style) so rotation is correct and the O piece no longer drifts on rotate; keep simple wall-kick offsets. - Both: render immediately after processing input (not only on gravity steps) so rotation/movement is no longer laggy. - Both: draw the window frame inset by 2-3px (double line) so the right and bottom edges are actually visible instead of clipped at the edge. --- overlay/rootfs/usr/bin/snake | Bin 13672 -> 13672 bytes overlay/rootfs/usr/bin/tetris | Bin 13668 -> 13668 bytes prebuilt/games/usr/bin/snake | Bin 13672 -> 13672 bytes prebuilt/games/usr/bin/tetris | Bin 13668 -> 13668 bytes src/games/snake.cpp | 8 +++-- src/games/tetris.cpp | 58 +++++++++++++++++++--------------- 6 files changed, 38 insertions(+), 28 deletions(-) diff --git a/overlay/rootfs/usr/bin/snake b/overlay/rootfs/usr/bin/snake index 8b4621c83f9298570e2b058c4ac5a7952a0b0a5f..5553412e604921e938301e14badcd88fca59251b 100755 GIT binary patch delta 3142 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 O625A-vVK%=qW%}^zwf#L delta 5438 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 O625A-vVK%=qW%}^zwf#L delta 5438 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 0) { @@ -363,6 +364,7 @@ int main() { FD_ZERO(&fds); FD_SET(xfd, &fds); int r = select(xfd + 1, &fds, nullptr, nullptr, &tv); + bool dirty = false; if (r > 0) { XEvent e; while (XPending(dpy)) { @@ -371,13 +373,15 @@ int main() { if (!running) break; } if (!running) break; + dirty = true; } if (running && now_ms() >= nextTick) { if (state == PLAYING) step(); if (state == PLAYING) nextTick = now_ms() + speed; else nextTick = now_ms() + 80; // keep loop responsive while paused/over - render(); + dirty = true; } + if (dirty) render(); } XFreeGC(dpy, gc); diff --git a/src/games/tetris.cpp b/src/games/tetris.cpp index cd130a1..4adf8f3 100644 --- a/src/games/tetris.cpp +++ b/src/games/tetris.cpp @@ -92,14 +92,16 @@ static unsigned long C_BG, C_FG, C_HEAD, C_FOOD, C_DIM; // ---- tetromino definitions (state 0 within a 4x4 box) ---- // type 0..6 -> I,O,T,S,Z,J,L -static const int BASE[7][4][2] = { - {{0,1},{1,1},{2,1},{3,1}}, // I - {{1,0},{2,0},{1,1},{2,1}}, // O - {{1,0},{0,1},{1,1},{2,1}}, // T - {{1,0},{2,0},{0,1},{1,1}}, // S - {{0,0},{1,0},{1,1},{2,1}}, // Z - {{0,0},{0,1},{1,1},{2,1}}, // J - {{2,0},{0,1},{1,1},{2,1}}, // L +// 7 tetrominoes, each with 4 explicit rotation states (classic SRS-style). +// type 0..6 -> I,O,T,S,Z,J,L. Coordinates are within a 4x4 box [x,y]. +static const int SHAPES[7][4][4][2] = { + { {{0,1},{1,1},{2,1},{3,1}}, {{2,0},{2,1},{2,2},{2,3}}, {{0,2},{1,2},{2,2},{3,2}}, {{1,0},{1,1},{1,2},{1,3}} }, // I + { {{1,0},{2,0},{1,1},{2,1}}, {{1,0},{2,0},{1,1},{2,1}}, {{1,0},{2,0},{1,1},{2,1}}, {{1,0},{2,0},{1,1},{2,1}} }, // O + { {{1,0},{0,1},{1,1},{2,1}}, {{1,0},{1,1},{2,1},{1,2}}, {{0,1},{1,1},{2,1},{1,2}}, {{1,0},{0,1},{1,1},{1,2}} }, // T + { {{1,0},{2,0},{0,1},{1,1}}, {{1,0},{1,1},{2,1},{2,2}}, {{1,1},{2,1},{0,2},{1,2}}, {{0,0},{0,1},{1,1},{1,2}} }, // S + { {{0,0},{1,0},{1,1},{2,1}}, {{2,0},{1,1},{2,1},{1,2}}, {{0,1},{1,1},{1,2},{2,2}}, {{1,0},{0,1},{1,1},{0,2}} }, // Z + { {{0,0},{0,1},{1,1},{2,1}}, {{1,0},{2,0},{1,1},{1,2}}, {{0,1},{1,1},{2,1},{2,2}}, {{1,0},{1,1},{0,2},{1,2}} }, // J + { {{2,0},{0,1},{1,1},{2,1}}, {{1,0},{1,1},{1,2},{2,2}}, {{0,1},{1,1},{2,1},{0,2}}, {{0,0},{1,0},{1,1},{1,2}} }, // L }; enum State { PLAYING, PAUSED, GAMEOVER }; @@ -112,16 +114,12 @@ static int speed = 1000; // ms per gravity step static unsigned long nextTick = 0; static bool running = true; -static void rotate_cells(const int src[4][2], int dst[4][2], int times) { - for (int i = 0; i < 4; i++) { - int x = src[i][0], y = src[i][1]; - for (int t = 0; t < times; t++) { int nx = 3 - y; int ny = x; x = nx; y = ny; } - dst[i][0] = x; dst[i][1] = y; - } -} - static void piece_cells(int type, int rot, int out[4][2]) { - rotate_cells(BASE[type], out, ((rot % 4) + 4) % 4); + int r = ((rot % 4) + 4) % 4; + for (int i = 0; i < 4; i++) { + out[i][0] = SHAPES[type][r][i][0]; + out[i][1] = SHAPES[type][r][i][1]; + } } static bool collide(int px, int py, int cells[4][2]) { @@ -214,14 +212,18 @@ static void try_move(int dx) { } static void try_rotate() { + int nr = (curRot + 1) % 4; int cells[4][2]; - piece_cells(curType, (curRot + 1) % 4, cells); - // simple wall kick: try shift left/right if blocked - if (!collide(curX, curY, cells)) { curRot = (curRot + 1) % 4; return; } - if (!collide(curX - 1, curY, cells)) { curX--; curRot = (curRot + 1) % 4; return; } - if (!collide(curX + 1, curY, cells)) { curX++; curRot = (curRot + 1) % 4; return; } - if (!collide(curX - 2, curY, cells)) { curX -= 2; curRot = (curRot + 1) % 4; return; } - if (!collide(curX + 2, curY, cells)) { curX += 2; curRot = (curRot + 1) % 4; return; } + piece_cells(curType, nr, cells); + // wall kicks: try in place, then small horizontal shifts + int kicks[5] = {0, -1, 1, -2, 2}; + for (int k = 0; k < 5; k++) { + if (!collide(curX + kicks[k], curY, cells)) { + curX += kicks[k]; + curRot = nr; + return; + } + } } static void reset_game() { @@ -259,7 +261,8 @@ static void render() { // frame the whole window (right/bottom edges too) XSetForeground(dpy, gc, C_DIM); - XDrawRectangle(dpy, back, gc, 0, 0, winW - 1, winH - 1); + XDrawRectangle(dpy, back, gc, 2, 2, winW - 4, winH - 4); + XDrawRectangle(dpy, back, gc, 3, 3, winW - 6, winH - 6); // settled blocks for (int x = 0; x < PCOLS; x++) @@ -445,6 +448,7 @@ int main() { FD_ZERO(&fds); FD_SET(xfd, &fds); int r = select(xfd + 1, &fds, nullptr, nullptr, &tv); + bool dirty = false; if (r > 0) { XEvent e; while (XPending(dpy)) { @@ -453,13 +457,15 @@ int main() { if (!running) break; } if (!running) break; + dirty = true; } if (running && now_ms() >= nextTick) { if (state == PLAYING) step(); if (state == PLAYING) nextTick = now_ms() + speed; else nextTick = now_ms() + 80; - render(); + dirty = true; } + if (dirty) render(); } XFreeGC(dpy, gc);