#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <event.h>
#include <keyboard.h>
enum
{
NX = 40,
NY = 40,
NLEV = 10,
NSNK = 100,
UP = -1,
DOWN = 1,
LEFT = -1,
RIGHT = 1,
};
char levels[NLEV][NY][NX];
char board[NY][NX];
Image *csnake;
Image *cbrick;
Image *cgoal;
Rectangle rboard;
Point pscore;
Point scoresz;
Point dsnake;
Point snake[NSNK];
Point pgoal;
int grow;
int nsnake;
int bricksz;
int goal;
int tsleep;
long points;
int
loadlevels(char *path)
{
Biobuf *b;
int x, y, l;
char c;
if(path == nil)
return 0;
b = Bopen(path, OREAD);
if(b == nil){
fprint(2, "can't open %s: %r", path);
return 0;
}
memset(levels, 0, sizeof(levels));
x = y = l = 0;
while((c = Bgetc(b)) > 0){
switch(c) {
case ';':
/* no ';'-comments in the middle of a level */
while(Bgetc(b) != '\n')
;
break;
case '\n':
x = 0;
y++;
c = Bgetc(b);
if(c == '\n' || c == Beof){
/* end of level */
if(++l == NLEV)
goto Done;
x = 0;
y = 0;
} else
Bungetc(b);
break;
case ' ':
x++;
break;
case '#':
levels[l][y][x]++;
x++;
break;
default:
fprint(2, "invalid char in level %d: %c\n", l+1, c);
return 0;
}
}
Done:
Bterm(b);
return 1;
}
void
score(int p)
{
char buf[128];
points += p;
snprint(buf, sizeof(buf), "s:%.6ld", points);
draw(screen, Rpt(pscore, addpt(pscore, scoresz)), display->white, nil, ZP);
string(screen, pscore, display->black, ZP, font, buf);
}
void
drawsq(Image *b,Point p, Image *c)
{
Rectangle r;
r.min = Pt(rboard.min.x+p.x*bricksz, rboard.min.y+p.y*bricksz);
r.max.x = r.min.x+bricksz;
r.max.y = r.min.y+bricksz;
if(c){
draw(b, r, display->black, nil, ZP);
draw(b, insetrect(r, 1), c, nil, ZP);
}else
draw(b, r, display->white, nil, ZP);
}
void
drawboard(void)
{
int i, j;
draw(screen, screen->r, display->white, nil, ZP);
border(screen, rboard , -2, display->black, ZP);
for(i=0; i<NY; i++) for(j=0; j<NX; j++)
if(board[i][j])
drawsq(screen, Pt(j,i), cbrick);
drawsq(screen, pgoal, cgoal);
score(0);
}
int
mvsnake(void)
{
Point p;
int i;
p = addpt(snake[0], dsnake);
if(p.x>NX || p.y>NY || p.x<0 || p.y<0)
return 1;
if(board[p.y][p.x])
return 1;
for(i=0; i<nsnake; i++)
if(eqpt(snake[i], p))
return 1;
if(grow){
snake[nsnake] = snake[nsnake-1];
nsnake++;
grow--;
}else
drawsq(screen, snake[nsnake-1], nil);
for(i=nsnake-1; i>0; i--)
snake[i] = snake[i-1];
snake[0] = p;
drawsq(screen, snake[0], csnake);
if(eqpt(p, pgoal))
goal = 0;
flushimage(display, 1);
return 0;
}
void
randpt(Point *pt)
{
Point p;
for(;;){
p = Pt(nrand(NX), nrand(NY));
if(!board[p.y][p.x])
break;
}
pt->x = p.x;
pt->y = p.y;
}
int
play(void)
{
Event ev;
ulong timer;
int level, dt;
int i, e;
dt = 128;
level = goal = 0;
timer = etimer(0, tsleep);
for(;;){
if(goal == 0){
memmove(board, levels[level%NLEV], sizeof(board));
randpt(&pgoal);
for(;;){
randpt(&snake[0]);
if(!eqpt(snake[0], pgoal))
break;
}
switch(nrand(4)){
case 0:
dsnake = Pt(0, UP);
break;
case 1:
dsnake = Pt(0, DOWN);
break;
case 2:
dsnake = Pt(LEFT, 0);
break;
case 3:
dsnake = Pt(RIGHT, 0);
break;
}
nsnake = 1;
goal++;
grow += 10+level;
score(250*level++);
drawboard();
flushimage(display, 1);
}
e = event(&ev);
switch(e){
case Ekeyboard:
switch(ev.kbdc){
case Kup:
if(dsnake.y != DOWN)
dsnake = Pt(0, UP);
break;
case Kdown:
if(dsnake.y != UP)
dsnake = Pt(0, DOWN);
break;
case Kleft:
if(dsnake.x != RIGHT)
dsnake = Pt(LEFT, 0);
break;
case Kright:
if(dsnake.x != LEFT)
dsnake = Pt(RIGHT, 0);
break;
case 'p':
ekbd();
break;
case 'q':
return 0;
}
break;
default:
if(e==timer){
dt -= tsleep;
if(dt < 0){
i = 1;
dt = 32 * (points+nrand(10000)-5000) / 10000;
if(dt >= 64){
i += (dt-64)/32;
dt = 64;
}
dt = 100-dt;
while(i-- > 0)
if(mvsnake()){
ekbd();
return 1;
}
}
}
break;
}
}
}
void
eresized(int new)
{
Rectangle r;
int d;
if(new && getwindow(display, Refnone) < 0)
sysfatal("can't reattach to window: %r");
r = insetrect(screen->r, 2);
d = Dx(r)>Dy(r)? Dx(r): Dy(r);
bricksz = (d/2)/NX;
rboard = r;
rboard.min.x += (Dx(r)-bricksz*NX)/2;
rboard.min.y += (Dy(r)-bricksz*NY)/2;
rboard.max.x = rboard.min.x+NX*bricksz;
rboard.max.y = rboard.min.y+NY*bricksz;
pscore.x = rboard.min.x+8;
pscore.y = rboard.min.y-32;
scoresz = stringsize(font, "s:0000000000");
drawboard();
flushimage(display, 1);
}
void
main(int argc, char *argv[])
{
char *levelpath = "/sys/games/lib/snake/default.slc";
char *scorespath = "/sys/games/lib/snake/scores";
long starttime, endtime;
int scores;
tsleep = 50;
ARGBEGIN{
case 'f':
levelpath = ARGF();
if(levelpath == nil)
goto Usage;
break;
default:
goto Usage;
}ARGEND
if(argc){
Usage:
fprint(2, "usage: %s [-f file]\n", argv0);
exits("usage");
}
if(!loadlevels(levelpath))
sysfatal("can't load: %s: %r", levelpath);
bricksz = 24;
scores = open(scorespath, OWRITE);
if(scores < 0)
sysfatal("can't open %s: %r", scorespath);
if(initdraw(0,0,"snake") < 0)
sysfatal("initdraw failed: %r");
csnake = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DRed);
cbrick = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DGreyblue);
cgoal = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DGreen);
if(csnake==nil || cbrick==nil)
sysfatal("allocimage failed: %r");
starttime = time(0);
srand(starttime);
einit(Emouse|Ekeyboard);
eresized(0);
if(play()){
endtime = time(0);
fprint(scores, "%ld\t%s\t%lud\t%ld\n",
points, getuser(), starttime, endtime-starttime);
}
exits(nil);
}
|