Plan 9 from Bell Labs’s /usr/web/sources/contrib/fgb/root/sys/src/games/snake/snake.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#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);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.