shithub: gm4s

Download patch

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;
+	}
+}
--