added a vipe coded tetris gui

This commit is contained in:
Hannes
2026-06-10 21:58:50 +02:00
parent 7114cb3c12
commit 712df01465
7 changed files with 399 additions and 128 deletions
-128
View File
@@ -1,6 +1,5 @@
import random import random
from PIL import Image from PIL import Image
import math
n0 = [ n0 = [
[1,1,1], [1,1,1],
@@ -82,62 +81,6 @@ n9 = [
[1,1,1] [1,1,1]
] ]
# a_a = [
# [1,1,1],
# [1,0,1],
# [1,1,1],
# [1,0,1],
# [0,0,0]
# ]
# a_b = [
# [1,1,0],
# [1,1,1],
# [1,0,1],
# [1,1,1],
# [0,0,0]
# ]
# a_c = [
# [1,1,1],
# [1,0,0],
# [1,0,0],
# [1,1,1],
# [0,0,0]
# ]
# a_e = [
# [1,1,1],
# [1,1,0],
# [1,0,0],
# [1,1,1],
# [0,0,0]
# ]
# a_o = [
# [1,1,1],
# [1,0,1],
# [1,0,1],
# [1,1,1],
# [0,0,0]
# ]
# a_r = [
# [1,1,1],
# [1,0,1],
# [1,1,0],
# [1,0,1],
# [0,0,0]
# ]
# a_s = [
# [1,1,1],
# [1,0,0],
# [0,1,1],
# [1,1,1],
# [0,0,0]
# ]
### FONT: https://github.com/teryror/pixel-fonts/tree/master ### FONT: https://github.com/teryror/pixel-fonts/tree/master
a_s = [ a_s = [
@@ -240,19 +183,6 @@ class color:
r = other[0] r = other[0]
g = other[1] g = other[1]
b = other[2] b = other[2]
# if self.r:
# new_r = (self.r + r) >> 1
# else:
# new_r = self.r
# if self.g:
# new_g = (self.g + g) >> 1
# else:
# new_g = self.g
# if self.b:
# new_b = (self.b + b) >> 1
# else:
# new_b = self.b
# return (new_r, new_g, new_b)
return (((self.r * r) >> 8), ((self.g * g) >> 8), ((self.b * b)>>8)) return (((self.r * r) >> 8), ((self.g * g) >> 8), ((self.b * b)>>8))
def shiftr(self, n): def shiftr(self, n):
return ((self.r >> n), (self.g >> n), (self.b >>n)) return ((self.r >> n), (self.g >> n), (self.b >>n))
@@ -314,11 +244,6 @@ def make_score():
score = random.randint(0, 999999999) score = random.randint(0, 999999999)
return score return score
# red = color(255, 0, 0, 255)
# blue = color(0, 0, 255, 255)
# green = color(0, 255, 0, 255)
# purple = color(160, 32, 240, 255)
# orange = color(255, 165, 0, 255)
black = color("#000000") black = color("#000000")
white = color("#ffffff") white = color("#ffffff")
@@ -337,10 +262,6 @@ sky_blue = color("#0080ff")
purple = color("#9d00ff") purple = color("#9d00ff")
lime = color("#5fdf5f") lime = color("#5fdf5f")
colors = [ colors = [
black, black,
cyan, cyan,
@@ -417,15 +338,6 @@ for y in range(height):
pixels[x, y] = colors[board[yd>>5][xd>>5]].mask(0xFFFFFFD7) pixels[x, y] = colors[board[yd>>5][xd>>5]].mask(0xFFFFFFD7)
else: else:
pixels[x, y] = colors[board[yd>>5][xd>>5]].mask(0xFFFFFFC0) pixels[x, y] = colors[board[yd>>5][xd>>5]].mask(0xFFFFFFC0)
# if (xs == 31) or (ys == 31):
# if (xs == 0) or (ys == 0):
# pixels[x, y] = colors[board[yd>>5][xd>>5]].shiftr(1)
# else:
# pixels[x, y] = colors[board[yd>>5][xd>>5]].shiftr(2)
# elif (not xs) or (not ys):
# pixels[x, y] = colors[board[yd>>5][xd>>5]].get()
# else:
# pixels[x, y] = colors[board[yd>>5][xd>>5]].mask(0xFFFFFFC0)
### Next Piece ### Next Piece
elif x>=nb1.x and x<nb1.x+nb1.width and y>=nb1.y and y<nb1.y+nb1.height: elif x>=nb1.x and x<nb1.x+nb1.width and y>=nb1.y and y<nb1.y+nb1.height:
xd = x-nb1.x xd = x-nb1.x
@@ -499,51 +411,11 @@ for y in range(height):
pixels[x, y] = text_bg.get() pixels[x, y] = text_bg.get()
elif x>=sn2.x and x<sn2.x+sn2.width and y>=sn2.y and y<sn2.y+sn2.height: elif x>=sn2.x and x<sn2.x+sn2.width and y>=sn2.y and y<sn2.y+sn2.height:
pixels[x, y] = text_bg.get() pixels[x, y] = text_bg.get()
# ####################### player 2
# ### Board
# elif x>=b2.x and x<b2.x+b2.width and y>=b2.y and y<b2.y+b2.height:
# xd = x-b2.x
# yd = y-b2.y
# pixels[x, y] = colors2[board2[yd>>5][xd>>5]].multiply(sprite_pixels[yd&0b0000000000011111, xd&0b0000000000011111])
# ### Next Piece
# elif x>=nb2.x and x<nb2.x+nb2.width and y>=nb2.y and y<nb2.y+nb2.height:
# xd = x-nb2.x
# yd = y-nb2.y
# pixels[x, y] = colors2[next_board[yd>>5][xd>>5]].multiply(sprite_pixels[yd&0b0000000000011111, xd&0b0000000000011111])
####################### BG
else: else:
if (x+y)&1: if (x+y)&1:
pixels[x, y] = (0, 0, 255) pixels[x, y] = (0, 0, 255)
else: else:
pixels[x, y] = ((x+y)>>3, (((1280+720)>>3)-((x+y)>>3)), 0) pixels[x, y] = ((x+y)>>3, (((1280+720)>>3)-((x+y)>>3)), 0)
# offset = 0
# numbers = [a_s, n0, n1, n2, n3, n4, n5, n6, n7, n8, n9]
# for num in numbers:
# for y in range(len(num)<<2):
# for x in range(len(num[y>>2])<<2):
# if num[y>>2][x>>2]:
# pixels[x+offset, y] = white.get()
# else:
# pixels[x+offset, y] = black.get()
# offset += (len(num[0])<<2)+4
# letters = [a_s, a_c, a_o, a_r, a_e]
# offset = 0
# base_offset = 720-32
# for letter in letters:
# for y in range(len(letter)<<2):
# for x in range(len(letter[y>>2])<<2):
# if letter[y>>2][x>>2]:
# pixels[x+offset, y+base_offset] = white.get()
# else:
# pixels[x+offset, y+base_offset] = black.get()
# offset += (len(letter[0])<<2) + 4
img.save("output/alpha.png") img.save("output/alpha.png")
img.show() img.show()
BIN
View File
Binary file not shown.
+99
View File
@@ -0,0 +1,99 @@
#include "gpu.h"
#include <string.h>
#include <unistd.h>
static void clear(int b[20][10]) {
memset(b, 0, 20 * 10 * sizeof(int));
}
static void set(int b[20][10], int r, int c, int v) {
if (r >= 0 && r < 20 && c >= 0 && c < 10) b[r][c] = v;
}
static void rect(int b[20][10], int r, int c, int h, int w, int v) {
for (int dr = 0; dr < h; dr++)
for (int dc = 0; dc < w; dc++)
set(b, r + dr, c + dc, v);
}
/* tetromino shapes: flat arrays of 8 ints (4 cells * 2 coords) */
static const int PIECE_T[] = {0,1, 1,0, 1,1, 1,2};
static const int PIECE_L[] = {0,0, 0,1, 0,2, 1,0};
static const int PIECE_J[] = {0,0, 0,1, 0,2, 1,2};
static const int PIECE_S[] = {0,1, 0,2, 1,0, 1,1};
static const int PIECE_Z[] = {0,0, 0,1, 1,1, 1,2};
static const int PIECE_O[] = {0,0, 0,1, 1,0, 1,1};
static const int PIECE_I[] = {0,0, 1,0, 2,0, 3,0};
static const int *PIECES[7] = {PIECE_T, PIECE_L, PIECE_J, PIECE_S, PIECE_Z, PIECE_O, PIECE_I};
static const int PCOLORS[7] = {5, 3, 2, 1, 7, 4, 6};
static void place_piece(int b[20][10], int r, int c, int pi, int v) {
const int *cells = PIECES[pi % 7];
for (int i = 0; i < 4; i++)
set(b, r + cells[i*2], c + cells[i*2+1], v);
}
static void fill_next(int n[4][4], int pi) {
memset(n, 0, 4 * 4 * sizeof(int));
const int *cells = PIECES[pi % 7];
for (int i = 0; i < 4; i++)
n[cells[i*2]][cells[i*2+1]] = PCOLORS[pi % 7];
}
int main() {
gpu_t *gpu = gpu_init();
if (!gpu) { fprintf(stderr, "failed to launch gpu\n"); return 1; }
int b1[20][10], b2[20][10], n1[4][4], n2[4][4];
clear(b1);
clear(b2);
/* --- Player 1: stack with pieces on top --- */
/* bottom layer */
rect(b1, 16, 0, 4, 10, 1);
rect(b1, 15, 1, 1, 8, 2);
rect(b1, 14, 2, 1, 6, 3);
rect(b1, 13, 3, 1, 4, 4);
rect(b1, 12, 4, 1, 2, 5);
/* a Tetromino falling */
place_piece(b1, 2, 3, 0, PCOLORS[0]); /* T piece */
/* --- Player 2: different palette, similar stack --- */
rect(b2, 16, 0, 4, 10, 1);
rect(b2, 15, 2, 1, 6, 2);
rect(b2, 14, 3, 1, 4, 3);
rect(b2, 13, 4, 1, 2, 4);
rect(b2, 12, 5, 1, 1, 5);
place_piece(b2, 3, 4, 3, PCOLORS[3]); /* S piece */
/* --- Next pieces --- */
fill_next(n1, 2); /* J */
fill_next(n2, 5); /* O */
gpu_update(gpu, b1, b2, n1, n2);
/* Animate: cycle through pieces falling on player 1 */
for (int frame = 0; frame < 60; frame++) {
usleep(100000); /* 100ms */
clear(b1);
rect(b1, 16, 0, 4, 10, 1);
rect(b1, 15, 1, 1, 8, 2);
rect(b1, 14, 2, 1, 6, 3);
rect(b1, 13, 3, 1, 4, 4);
rect(b1, 12, 4, 1, 2, 5);
int pi = frame / 8 % 7;
int row = 2 + (frame % 8);
place_piece(b1, row, 3, pi, PCOLORS[pi]);
fill_next(n1, (pi + 1) % 7);
gpu_update(gpu, b1, b2, n1, n2);
}
gpu_close(gpu);
return 0;
}
+91
View File
@@ -0,0 +1,91 @@
#ifndef GPU_H
#define GPU_H
#include <stdio.h>
#include <stdlib.h>
#define GPU_BOARD_H 20
#define GPU_BOARD_W 10
#define GPU_NEXT_H 4
#define GPU_NEXT_W 4
#ifndef GPU_SCRIPT
#define GPU_SCRIPT "/home/honney/Projects/tetris_gui/gpu.py"
#endif
typedef struct {
FILE *pipe;
} gpu_t;
static gpu_t *gpu_init(void) {
gpu_t *g = (gpu_t *)malloc(sizeof(gpu_t));
if (!g) return NULL;
g->pipe = popen(GPU_SCRIPT, "w");
if (!g->pipe) { free(g); return NULL; }
return g;
}
static void gpu_update(gpu_t *g,
int b1[GPU_BOARD_H][GPU_BOARD_W],
int b2[GPU_BOARD_H][GPU_BOARD_W],
int n1[GPU_NEXT_H][GPU_NEXT_W],
int n2[GPU_NEXT_H][GPU_NEXT_W]) {
FILE *p = g->pipe;
fputs("{\"b1\":[", p);
for (int r = 0; r < GPU_BOARD_H; r++) {
if (r) fputc(',', p);
fputc('[', p);
for (int c = 0; c < GPU_BOARD_W; c++) {
if (c) fputc(',', p);
fprintf(p, "%d", b1[r][c]);
}
fputc(']', p);
}
fputs("],\"b2\":[", p);
for (int r = 0; r < GPU_BOARD_H; r++) {
if (r) fputc(',', p);
fputc('[', p);
for (int c = 0; c < GPU_BOARD_W; c++) {
if (c) fputc(',', p);
fprintf(p, "%d", b2[r][c]);
}
fputc(']', p);
}
fputs("],\"n1\":[", p);
for (int r = 0; r < GPU_NEXT_H; r++) {
if (r) fputc(',', p);
fputc('[', p);
for (int c = 0; c < GPU_NEXT_W; c++) {
if (c) fputc(',', p);
fprintf(p, "%d", n1[r][c]);
}
fputc(']', p);
}
fputs("],\"n2\":[", p);
for (int r = 0; r < GPU_NEXT_H; r++) {
if (r) fputc(',', p);
fputc('[', p);
for (int c = 0; c < GPU_NEXT_W; c++) {
if (c) fputc(',', p);
fprintf(p, "%d", n2[r][c]);
}
fputc(']', p);
}
fputs("]}\n", p);
fflush(p);
}
static void gpu_close(gpu_t *g) {
if (g) {
if (g->pipe) fclose(g->pipe);
free(g);
}
}
#endif
+182
View File
@@ -0,0 +1,182 @@
#!/home/honney/Projects/tetris_gui/.venv/bin/python3
import sys, json, select, os
import pygame
WIDTH, HEIGHT = 1280, 720
CELL = 32
BOARD_H, BOARD_W = 20, 10
NEXT_H, NEXT_W = 4, 4
B1 = (304, 40, 320, 640)
B2 = (656, 40, 320, 640)
N1 = (88, 96, 128, 128)
N2 = (1064, 96, 128, 128)
S1 = (84, 454, 136, 32)
S2 = (1060, 454, 136, 32)
SCORE_FONT = [
[ # S
[0,1,1,1,1,0],[1,1,0,0,1,1],[1,1,0,0,0,0],
[0,1,1,1,0,0],[0,0,1,1,1,0],[0,0,0,0,1,1],
[1,1,0,0,1,1],[0,1,1,1,1,0],
],
[ # C
[0,1,1,1,1,0],[1,1,1,0,1,1],[1,1,0,0,0,0],
[1,1,0,0,0,0],[1,1,0,0,0,0],[1,1,0,0,0,0],
[1,1,1,0,1,1],[0,1,1,1,1,0],
],
[ # O
[0,1,1,1,1,0],[1,1,0,0,1,1],[1,1,0,0,1,1],
[1,1,0,0,1,1],[1,1,0,0,1,1],[1,1,0,0,1,1],
[1,1,0,0,1,1],[0,1,1,1,1,0],
],
[ # R
[1,1,1,1,1,0],[1,1,0,0,1,1],[1,1,0,0,1,1],
[1,1,1,1,1,0],[1,1,0,1,1,1],[1,1,0,0,1,1],
[1,1,0,0,1,1],[1,1,0,0,1,1],
],
[ # E
[1,1,1,1,1,1],[1,1,0,0,0,0],[1,1,0,0,0,0],
[1,1,1,1,1,1],[1,1,0,0,0,0],[1,1,0,0,0,0],
[1,1,0,0,0,0],[1,1,1,1,1,1],
],
]
PALETTE = [
(0,0,0),(0,255,255),(0,0,255),(255,128,0),
(255,242,0),(0,255,0),(159,0,255),(255,0,0),
]
PALETTE_ALT = [
(0,0,0),(0,134,243),(61,79,220),(253,171,1),
(171,203,4),(2,189,205),(210,15,99),(254,75,15),
]
def bevel_color(r, g, b, x, y, style):
if style == 0:
if 8 <= x < 24 and 8 <= y < 24:
return (r, g, b)
if 6 <= x < 26 and 6 <= y < 26:
return (r & 0xB0, g & 0xB0, b & 0xB0)
if x + y < 31:
return (r, g, b) if x > y else (r & 0xEF, g & 0xEF, b & 0xEF)
return (r & 0xD7, g & 0xD7, b & 0xD7) if x > y else (r & 0xC0, g & 0xC0, b & 0xC0)
if x == 31 or y == 31:
return (r >> 1, g >> 1, b >> 1) if (x == 0 or y == 0) else (r >> 2, g >> 2, b >> 2)
if x == 0 or y == 0:
return (r, g, b)
return (r & 0xC0, g & 0xC0, b & 0xC0)
def build_blocks(palette, style):
tex = []
for clr in palette:
surf = pygame.Surface((CELL, CELL))
for y in range(CELL):
for x in range(CELL):
surf.set_at((x, y), bevel_color(*clr, x, y, style))
tex.append(surf)
return tex
def build_background():
surf = pygame.Surface((WIDTH, HEIGHT))
for y in range(HEIGHT):
for x in range(WIDTH):
if (x + y) & 1:
surf.set_at((x, y), (0, 0, 255))
else:
v = (x + y) >> 3
surf.set_at((x, y), (v, 250 - v, 0))
return surf
def build_score():
surf = pygame.Surface((136, 32))
surf.fill((0, 0, 0))
x_off = 0
for glyph in SCORE_FONT:
for fy, row in enumerate(glyph):
for fx, px in enumerate(row):
if px:
pygame.draw.rect(surf, (255, 255, 255), (x_off + fx * 4, fy * 4, 4, 4))
x_off += 28
return surf
class Display:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Tetris")
self.bg = build_background()
self.score = build_score()
self.b1_tex = build_blocks(PALETTE, 0)
self.b2_tex = build_blocks(PALETTE_ALT, 0)
self.n1_tex = build_blocks(PALETTE, 1)
self.n2_tex = build_blocks(PALETTE_ALT, 1)
self.board1 = [[0] * BOARD_W for _ in range(BOARD_H)]
self.board2 = [[0] * BOARD_W for _ in range(BOARD_H)]
self.next1 = [[0] * NEXT_W for _ in range(NEXT_H)]
self.next2 = [[0] * NEXT_W for _ in range(NEXT_H)]
def draw_grid(self, board, rect, textures):
x, y, w, h = rect
for row in range(h // CELL):
for col in range(w // CELL):
v = board[row][col]
self.screen.blit(textures[v if 0 <= v <= 7 else 0], (x + col * CELL, y + row * CELL))
def render(self):
self.screen.blit(self.bg, (0, 0))
self.draw_grid(self.board1, B1, self.b1_tex)
self.draw_grid(self.board2, B2, self.b2_tex)
self.draw_grid(self.next1, N1, self.n1_tex)
self.draw_grid(self.next2, N2, self.n2_tex)
self.screen.blit(self.score, (S1[0], S1[1]))
self.screen.blit(self.score, (S2[0], S2[1]))
pygame.display.flip()
def close(self):
pygame.quit()
def main():
disp = Display()
buf = ""
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if not sys.stdin.isatty():
r, _, _ = select.select([sys.stdin], [], [], 0)
if r:
chunk = os.read(sys.stdin.fileno(), 65536)
if not chunk:
running = False
else:
buf += chunk.decode("utf-8")
while "\n" in buf:
line, buf = buf.split("\n", 1)
line = line.strip()
if not line:
continue
try:
data = json.loads(line)
if "b1" in data:
disp.board1 = data["b1"]
if "b2" in data:
disp.board2 = data["b2"]
if "n1" in data:
disp.next1 = data["n1"]
if "n2" in data:
disp.next2 = data["n2"]
except json.JSONDecodeError:
pass
disp.render()
clock.tick(60)
disp.close()
sys.exit()
if __name__ == "__main__":
main()
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
VENV_DIR="$SCRIPT_DIR/.venv"
if [ -d "$VENV_DIR" ]; then
echo "Removing existing venv ..."
rm -rf "$VENV_DIR"
fi
echo "Creating venv at $VENV_DIR ..."
python3 -m venv "$VENV_DIR" --without-pip
echo "Bootstrapping pip ..."
curl -sL https://bootstrap.pypa.io/get-pip.py | "$VENV_DIR/bin/python3"
echo "Installing dependencies ..."
"$VENV_DIR/bin/pip" install -r "$SCRIPT_DIR/requirements.txt"
echo "Fixing shebang in gpu.py ..."
if [ -f "$SCRIPT_DIR/gpu.py" ]; then
sed -i "1s|^#!.*|#!$VENV_DIR/bin/python3|" "$SCRIPT_DIR/gpu.py"
fi
echo "Done. Activate with: source $VENV_DIR/bin/activate"
+1
View File
@@ -0,0 +1 @@
pygame>=2.5.0