ref: 9bb3552ed63f940cab219c6a0710f2d5b3cb22b7
dir: /game.c/
#include <u.h>
#include <libc.h>
#include "dat.h"
#include "fns.h"
#include "/sys/src/games/eui.h"
/* FIXME: grace period before freeze */
char playfield[Ncol * Nrow];
Current *cur;
int next[4];
int held = -1;
enum{
Nlineperlvl = 10,
Timeinc = BILLION / 10.0,
Thover = 400 * MILLION,
};
static vlong ncleared;
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 int
nextpiece(void)
{
int r, *p;
r = next[0];
for(p=next; p<next+nelem(next)-1; p++)
p[0] = p[1];
*p = getpiece();
return r;
}
static void
spawn(void)
{
static Current cur0;
memset(&cur0, 0, sizeof cur0);
cur0.type = nextpiece();
cur0.rot = Up;
cur0.x = Ncol / 2 - 2;
cur0.y = Nstartrow - Nextrarows - 1;
cur = &cur0;
if(collide(cur->x, cur->y, cur->rot))
gameover();
}
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)
{
double t;
if(++ncleared % Nlineperlvl != 0)
return;
if((t = T - Timeinc) >= Timeinc)
T = t;
else if(T > Timeinc)
T = Timeinc;
else
T *= 0.9;
}
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;
}
/* FIXME: uneven next step delay since we don't reset time in ticproc */
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)
{
u64int t;
if(cur == nil){
spawn();
return;
}else if(collide(cur->x, cur->y+1, cur->rot)){
t = nanosec();
if(cur->lastmove == 0){
cur->lastmove = t;
cur->flags |= Fhovering;
}else if((t - cur->lastmove) >= Thover
|| collide(cur->x-1, cur->y, cur->rot)
&& collide(cur->x+1, cur->y, cur->rot))
freeze();
return;
}
cur->flags &= ~Fhovering;
cur->lastmove = 0;
cur->y++;
}
void
initgame(void)
{
int *p;
for(p=next; p<next+nelem(next); p++)
*p = getpiece();
}