ref: c646cc00096a81fa0d9a774324112e21906d56fa
author: qwx <qwx@sciops.net>
date: Sat Mar 1 21:47:34 EST 2025
you know what this commit is
--- /dev/null
+++ b/dat.h
@@ -1,0 +1,62 @@
+typedef struct Current Current;
+enum{
+ FI,
+ FZ,
+ FS,
+ FJ,
+ FL,
+ FO,
+ FT,
+ NF,
+
+ Up = 0,
+ Right,
+ Down,
+ Left,
+ Nrot,
+
+ Nside = 4,
+};
+enum{
+ Fswapped = 1<<0,
+};
+struct Current{
+ int x;
+ int y;
+ int type;
+ int rot;
+ int flags;
+ double thover;
+};
+extern Current *cur;
+extern int fours[NF][Nrot];
+
+enum{
+ Nrow = 40,
+ Nstartrow = 20,
+ Nextrarows = 2,
+ Ncol = 10,
+ Block = 16,
+
+ Wwidth = Block * Ncol,
+ Wheight = Block * (Nstartrow + Nextrarows),
+
+ K← = 1<<0,
+ K→ = 1<<1,
+ K↑ = 1<<2,
+ K↓ = 1<<3,
+ Kmove = K← | K→ | K↓,
+ Krotl = 1<<4,
+ Krotr = 1<<5,
+ Khold = 1<<6,
+
+ Tspeed0 = 1, /* seconds */
+};
+#define T0 (double)BILLION / Tspeed0
+extern char playfield[Ncol * Nrow];
+extern double T;
+
+enum{
+ DOrange = 0xffff00ff,
+ DPurple = 0xff00ffff,
+};
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,8 @@
+int collide(int, int, int);
+void hold(void);
+void drop(void);
+void gameover(void);
+void step(void);
+void redraw(void);
+void initimg(void);
+void quit(void);
--- /dev/null
+++ b/game.c
@@ -1,0 +1,193 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+char playfield[Ncol * Nrow];
+Current *cur;
+
+enum{
+ Nlineperlvl = 10,
+};
+static vlong ncleared;
+static int held = -1;
+
+static int bfield[Nrow];
+
+static u32int
+trand(void)
+{
+ static u32int x = 0xffffffff;
+
+ if(x == 0xffffffff)
+ x = rand();
+ x = x * 0x41c64e6d + 0x3039 >> 10 & 0x7fff;
+ return x;
+}
+
+static int
+getpiece(void)
+{
+ int i, *h, r;
+ static int hist[4] = {FZ, FZ, FS, FS};
+
+ for(r=i=0; i<5; i++){
+ r = trand() % 7;
+ for(h=hist; h<hist+nelem(hist); h++)
+ if(*h == r)
+ break;
+ if(h == hist + nelem(hist))
+ break;
+ r = trand() % 7;
+ }
+ hist[3] = hist[2];
+ hist[2] = hist[1];
+ hist[1] = hist[0];
+ hist[0] = r;
+ return r;
+}
+
+static void
+spawn(void)
+{
+ static Current cur0;
+
+ memset(&cur0, 0, sizeof cur0);
+ cur0.type = getpiece();
+ cur0.rot = Up;
+ cur0.x = Ncol / 2 - 2;
+ cur0.y = Nstartrow - Nextrarows - 1;
+ cur = &cur0;
+ if(collide(cur->x, cur->y, cur->rot))
+ gameover();
+}
+
+/* FIXME: gm4s: freeze: overlapping piece type 2 0,21 at 0,21:
+ * and I piece horizontal clipped past the wall to the right prior */
+
+int
+collide(int x, int y, int rot)
+{
+ int n, m, s, b, l, *pp, f;
+
+ switch(x){
+ case -3: b = 0b1110111011101110; s = Ncol - 1; m = 1; break;
+ case -2: b = 0b1100110011001100; s = Ncol - 2; m = 3; break;
+ case -1: b = 0b1000100010001000; s = Ncol - 3; m = 7; break;
+ case Ncol-3: b = 0b0001000100010001; s = 1; m = 14; break;
+ case Ncol-2: b = 0b0011001100110011; s = 2; m = 12; break;
+ case Ncol-1: b = 0b0111011101110111; s = 3; m = 8; break;
+ default: b = 0; s = Ncol - Nside - x; m = 15; break;
+ }
+ f = fours[cur->type][rot];
+ pp = bfield + y;
+ for(n=12; n>=0; n-=4, y++){
+ if(y >= Nrow)
+ l = 15;
+ else if(x >= Ncol - 3)
+ l = *pp++ << s & m;
+ else
+ l = *pp++ >> s & m;
+ b |= l << n;
+ }
+ return b & f;
+}
+
+static void
+updatelevel(void)
+{
+ if(++ncleared % Nlineperlvl != 0)
+ return;
+ T *= 0.9; /* FIXME: linear makes more sense */
+}
+
+static void
+clearlines(void)
+{
+ int b, *bf;
+
+ for(bf=bfield+nelem(bfield)-1, b=*bf; b!=0; b=*(--bf)){
+ if(b != (1 << Ncol) - 1)
+ continue;
+ memmove(bfield+1, bfield, (bf-bfield) * sizeof *bf);
+ memmove(playfield+Ncol, playfield, (bf-bfield) * Ncol);
+ bf++;
+ updatelevel();
+ }
+}
+
+void
+hold(void)
+{
+ int p;
+
+ if(cur->flags & Fswapped)
+ return;
+ p = held;
+ held = cur->type;
+ spawn();
+ if(p != -1)
+ cur->type = p;
+ cur->flags |= Fswapped;
+}
+
+static void
+freeze(void)
+{
+ char *p;
+ int n, x, y;
+ u32int f;
+
+ if((y = cur->y) >= Nrow)
+ sysfatal("freeze: oob 1 piece type %d at %d,%d",
+ cur->type, cur->x, cur->y);
+ f = fours[cur->type][cur->rot];
+ p = playfield + y * Ncol + cur->x;
+ for(n=0, x=1<<(Nside*Nside-1); x>0; x>>=1, p++){
+ if(f & x){
+ if(y >= Nrow)
+ sysfatal("freeze: oob 2 piece type %d %d,%d at %zd,%d",
+ cur->type, cur->x, cur->y, p-(playfield+y*Ncol), y);
+ if(*p != 0)
+ sysfatal("freeze: overlapping piece type %d %d,%d at %zd,%d",
+ cur->type, cur->x, cur->y, p-(playfield+y*Ncol), y);
+ *p = cur->type + 1;
+ bfield[y] |= 1 << (Ncol - 1 - cur->x - n);
+ }
+ if(++n == Nside){
+ p += Ncol - Nside;
+ n = 0;
+ y++;
+ }
+ }
+ clearlines();
+ cur = nil;
+}
+
+void
+drop(void)
+{
+ while(!collide(cur->x, cur->y+1, cur->rot))
+ cur->y++;
+ freeze();
+}
+
+void
+gameover(void)
+{
+ print("you're done! finished!\n");
+ quit();
+}
+
+void
+step(void)
+{
+ if(cur == nil){
+ spawn();
+ return;
+ }else if(collide(cur->x, cur->y+1, cur->rot)){
+ freeze();
+ return;
+ }
+ cur->y++;
+}
--- /dev/null
+++ b/gm4s.c
@@ -1,0 +1,135 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <keyboard.h>
+#include "/sys/src/games/eui.h"
+#include "dat.h"
+#include "fns.h"
+
+extern Channel *keychan;
+
+/* https://tetris.fandom.com/wiki/Playfield */
+
+double T = T0;
+
+static Channel *stepc, *evc;
+
+void
+quit(void)
+{
+ threadexitsall(nil);
+}
+
+static void
+kevent(ulong k)
+{
+ char r, rot;
+ static char rr[] = {Left, Up, Right, Down, Left, Up};
+
+ if(cur == nil)
+ return;
+ if(k & Khold)
+ hold();
+ rot = cur->rot;
+ if(k & Krotl && !collide(cur->x, cur->y, (r = rr[1+rot-1]))
+ || k & Krotr && !collide(cur->x, cur->y, (r = rr[1+rot+1])))
+ cur->rot = r;
+ if(k & K← && !collide(cur->x - 1, cur->y, rot))
+ cur->x--;
+ if(k & K→ && !collide(cur->x + 1, cur->y, rot))
+ cur->x++;
+ if(k & K↑)
+ drop();
+ if(k & K↓ && !collide(cur->x, cur->y + 1, rot))
+ cur->y++;
+}
+
+static void
+pollproc(void *)
+{
+ ulong k;
+
+ for(;;){
+ if(recv(keychan, &k) < 0)
+ return;
+ if(k == 0)
+ continue;
+ if(send(evc, &k) < 0)
+ return;
+ for(k&=Kmove; k&Kmove; k&=Kmove){
+ sleep(40);
+ if(nbrecv(keychan, &k) < 0)
+ return;
+ if(send(evc, &k) < 0)
+ return;
+ }
+ }
+}
+
+static void
+ticproc(void *)
+{
+ double t0;
+ vlong t, Δt;
+
+ t0 = nsec();
+ for(;;){
+ nbsendul(stepc, 1);
+ t = nsec();
+ Δt = t - t0;
+ t0 += T * (1 + Δt / T);
+ if(Δt < T)
+ sleep((T - Δt) / MILLION);
+ }
+}
+
+void
+threadmain(int argc, char **argv)
+{
+ ulong k;
+
+ ARGBEGIN{
+ }ARGEND
+ if((stepc = chancreate(sizeof(ulong), 1)) == nil
+ || (evc = chancreate(sizeof(ulong), 0)) == nil
+ || (keychan = chancreate(sizeof(ulong), 8)) == nil)
+ sysfatal("chancreate: %r");
+ /* FIXME: simultaneous kproc and joyproc in eui */
+ initemu(Wwidth, Wheight, 4, XRGB32, 1, nil);
+ initimg();
+ fmtinstall('H', encodefmt);
+ regkey("up", Kup, K↑);
+ regkey("down", Kdown, K↓);
+ regkey("left", Kleft, K←);
+ regkey("right", Kright, K→);
+ regkey("a", 'z', Krotl);
+ regkey("b", 'x', Krotr);
+ regkey("l1", ' ', Khold);
+ if(proccreate(pollproc, nil, 4096) < 0)
+ sysfatal("proccreate: %r");
+ if(proccreate(ticproc, nil, 4096) < 0)
+ sysfatal("proccreate: %r");
+ srand(time(nil));
+ enum{
+ Astep,
+ Akey,
+ };
+ Alt a[] = {
+ [Astep] {stepc, nil, CHANRCV},
+ [Akey] {evc, &k, CHANRCV},
+ {nil, nil, CHANEND}
+ };
+ for(;;){
+ switch(alt(a)){
+ default: threadexitsall(nil);
+ case Astep:
+ step();
+ break;
+ case Akey:
+ kevent(k);
+ break;
+ }
+ redraw();
+ }
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,13 @@
+</$objtype/mkfile
+BIN=$home/bin/$objtype
+TARG=gm4s
+OFILES=\
+ game.$O\
+ gm4s.$O\
+ piece.$O\
+ eui.$O\
+
+HFILES= dat.h fns.h
+</sys/src/cmd/mkone
+eui.$O: /sys/src/games/eui.c
+ $CC $CFLAGS $prereq
--- /dev/null
+++ b/piece.c
@@ -1,0 +1,158 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <pool.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+#include "/sys/src/games/eui.h"
+
+int fours[NF][Nrot] = {
+ [FI] {
+ 0b0000111100000000,
+ 0b0010001000100010,
+ 0b0000000011110000,
+ 0b0100010001000100,
+ },
+ [FJ] {
+ 0b1000111000000000,
+ 0b0110010001000000,
+ 0b0000111000100000,
+ 0b0100010011000000,
+ },
+ [FL] {
+ 0b0010111000000000,
+ 0b0100010001100000,
+ 0b0000111010000000,
+ 0b1100010001000000,
+ },
+ [FO] {
+ 0b0000011001100000,
+ 0b0000011001100000,
+ 0b0000011001100000,
+ 0b0000011001100000,
+ },
+ [FS] {
+ 0b1100011000000000,
+ 0b0010011001000000,
+ 0b0000110001100000,
+ 0b0100110010000000,
+ },
+ [FT] {
+ 0b0100111000000000,
+ 0b0100011001000000,
+ 0b0000111001000000,
+ 0b0100110001000000,
+ },
+ [FZ] {
+ 0b0110110000000000,
+ 0b0100011000100000,
+ 0b0000011011000000,
+ 0b1000110001000000,
+ },
+};
+
+static u32int cols[NF] = {
+ [FI] DCyan,
+ [FJ] DBlue,
+ [FL] DOrange,
+ [FO] DYellow,
+ [FS] DGreen,
+ [FT] DPurple,
+ [FZ] DRed,
+};
+static Image *piece[NF];
+
+enum{
+ Nline = Ncol * Block,
+};
+
+static void
+drawplayfield(void)
+{
+ int w;
+ u32int c, *s, *p, *pe;
+ char fc, *f, *fe;
+
+ w = Block * 1;
+ p = (u32int *)pic;
+ f = playfield + Ncol * (Nrow - Nstartrow - Nextrarows);
+ for(s=p, fe=playfield+nelem(playfield); f<fe; f++){
+ if((fc = *f) == 0)
+ //c = cols[nrand(nelem(cols))];
+ c = 0xff000000;
+ else
+ c = cols[fc - 1];
+ for(pe=p+w; p<pe; p++)
+ *p = c;
+ if(p - s == Ncol * w){
+ p += Ncol * w * (Block - 1);
+ s = p;
+ }
+ }
+}
+
+static void
+drawpiece(void)
+{
+ int f, x, n, w;
+ u32int c, *l, *s, *p, *pe;
+
+ if(cur == nil)
+ return;
+ s = (u32int *)pic;
+ s += (cur->y - Nstartrow + Nextrarows) * Nline * Block + cur->x * Block;
+ l = s;
+ c = cols[cur->type];
+ f = fours[cur->type][cur->rot];
+ w = Block * 1;
+ for(n=0, x=1<<(Nside*Nside-1); x>0; x>>=1){
+ if(s >= (u32int *)pic && f & x)
+ for(p=s, pe=p+w; p<pe; p++)
+ *p = c;
+ if(++n == Nside){
+ l += Block * Ncol * w;
+ s = l;
+ n = 0;
+ }else
+ s += w;
+ }
+}
+
+static void
+vscalepic(void)
+{
+ int n;
+ u32int *p, *s, *e;
+
+ n = 1 * Ncol * Block;
+ for(s=(u32int*)pic, e=s+n*Wheight; s<e; s=p)
+ for(p=s+n; p<s+n*Block; p+=n)
+ memcpy(p, s, n * sizeof *p);
+}
+
+void
+redraw(void)
+{
+ drawplayfield();
+ drawpiece();
+ vscalepic();
+ flushmouse(1);
+ flushscreen();
+}
+
+void
+initimg(void)
+{
+ u32int col, *c;
+ Rectangle r;
+ Image **i, **ie;
+
+ r = Rect(0, 0, Block, Block);
+ for(c=cols, i=piece, ie=i+nelem(piece); i<ie; i++, c++){
+ col = *c;
+ if((*i = allocimage(display, r, XRGB32, 0, col)) == nil)
+ sysfatal("allocimage: %r");
+ *c = col & 0xff << 24 | col >> 8 & 0xffffff;
+ }
+}
--
⑨