#include #include #include #include #include #include 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; iNX || p.y>NY || p.x<0 || p.y<0) return 1; if(board[p.y][p.x]) return 1; for(i=0; i0; 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); }