]> git.lizzy.rs Git - plan9front.git/commitdiff
added glendy game!
authoraiju <aiju@cirno>
Sun, 8 May 2011 18:59:10 +0000 (18:59 +0000)
committeraiju <aiju@cirno>
Sun, 8 May 2011 18:59:10 +0000 (18:59 +0000)
sys/src/games/glendy.c [new file with mode: 0644]
sys/src/games/mkfile

diff --git a/sys/src/games/glendy.c b/sys/src/games/glendy.c
new file mode 100644 (file)
index 0000000..753bd0a
--- /dev/null
@@ -0,0 +1,530 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+
+enum{
+       /* difficulty levels (how many circles are initially occupied) */
+       DEasy,  /* 10≤x<15 */
+       DMed,   /* 5≤x<10 */
+       DHard,  /* 0≤x<5 */
+
+       /* dynamic? original game has a fixed grid size, but we don't need to abide by it */
+       SzX = 11,
+       SzY = 11, 
+
+       Border = 10,
+       /* movement directions */
+       NE,
+       E,
+       SE,
+       SW,
+       W,
+       NW,
+
+       Won = 1,        /* game-ending states */
+       Lost = 2,
+};
+
+Font *font;
+
+int difficulty = DMed;
+int finished;
+
+int grid[SzX][SzY];
+int ogrid[SzX][SzY];   /* so we can restart levels */
+
+Image  *gl;    /* glenda */
+Image  *glm;   /* glenda's mask */
+Image  *cc; /* clicked */
+Image  *ec; /* empty; not clicked */
+Image  *bg;
+Image  *lost;
+Image  *won;
+
+
+char *mbuttons[] = 
+{
+       "easy",
+       "medium",
+       "hard",
+       0
+};
+
+char *rbuttons[] = 
+{
+       "new",
+       "reset",
+       "exit",
+       0
+};
+
+Menu mmenu = 
+{
+       mbuttons,
+};
+
+Menu rmenu =
+{
+       rbuttons,
+};
+
+Image *
+eallocimage(Rectangle r, int repl, uint color)
+{
+       Image *tmp;
+
+       tmp = allocimage(display, r, screen->chan, repl, color);
+       if(tmp == nil)
+               sysfatal("cannot allocate buffer image: %r");
+
+       return tmp;
+}
+
+Image *
+eloadfile(char *path)
+{
+       Image *img;
+       int fd;
+
+       fd = open(path, OREAD);
+       if(fd < 0) {
+               fprint(2, "cannot open image file %s: %r\n", path);
+               exits("image");
+       }
+       img = readimage(display, fd, 0);
+       if(img == nil)
+               sysfatal("cannot load image: %r");
+       close(fd);
+       
+       return img;
+}
+
+
+void
+allocimages(void)
+{
+       Rectangle one = Rect(0, 0, 1, 1);
+       
+       cc = eallocimage(one, 1, 0x777777FF);
+       ec = eallocimage(one, 1, DPalegreen);
+       bg = eallocimage(one, 1, DPurpleblue);
+       lost = eallocimage(one, 1, DRed);
+       won = eallocimage(one, 1, DGreen);
+       gl = eloadfile("/lib/face/48x48x4/g/glenda.1");
+
+       glm = allocimage(display, Rect(0, 0, 48, 48), gl->chan, 1, DCyan);
+       if(glm == nil)
+                       sysfatal("cannot allocate mask: %r");
+
+       draw(glm, glm->r, display->white, nil, ZP);
+       gendraw(glm, glm->r, display->black, ZP, gl, gl->r.min);
+       freeimage(gl);
+       gl = display->black;
+
+
+}
+
+/* unnecessary calculations here, but it's fine */
+Point
+board2pix(int x, int y)
+{
+       float d, rx, ry, yh;
+       int nx, ny;
+
+       d = (float)(Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20 : Dx(screen->r) -20;
+       rx = d/(float)SzX;
+       rx = rx/2.0;
+       ry = d/(float)SzY;
+       ry = ry/2.0;
+
+       yh = ry/3.73205082;
+
+       nx = (int)((float)x*rx*2.0+rx +(y%2?rx:0.0)); /* nx = x*(2rx) + rx + rx (conditional) */
+       ny = (int)((float)y*(ry*2.0-(y>0?yh:0.0)) + ry); /* ny = y*(2ry-yh) +ry */
+       return Pt(nx, ny);
+}
+
+Point 
+pix2board(int x, int y)
+{
+       float d, rx, ry, yh;
+       int ny, nx;
+
+       /* XXX: float→int causes small rounding errors */
+
+       d = (float)(Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20: Dx(screen->r)-20;
+       rx = d/(float)SzX;
+       rx = rx/2.0;
+       ry =d/(float)SzY;
+       ry = ry/2.0;
+
+       yh = ry/3.73205082;
+
+       /* reverse board2pix() */
+       ny = (int)(((float)y - ry)/(2*ry - ((y>2*ry)?yh:0.0)) + 0.5); /* ny = (y - ry)/(2ry-yh) */
+       nx = (int)(((float)x - rx - (ny%2?rx:0.0))/(rx*2.0) + 0.5); /* nx = (x - rx - rx)/2rx */
+       
+       if (nx >= SzX)
+               nx = SzX-1;
+       if (ny >=SzY)
+               ny = SzY-1;
+
+       return Pt(nx, ny);
+}
+
+void
+initlevel(void)
+{
+       int i, cnt = 10, x, y;
+
+       for(x = 0; x < SzX; x++)
+               for(y = 0; y < SzY; y++)
+                       ogrid[x][y] = 100;
+
+       switch(difficulty){
+       case DEasy:
+               cnt = 10 + nrand(5);
+               break;
+       case DMed:
+               cnt = 5 + nrand(5);
+               break;
+       case DHard:
+               cnt = nrand(5);
+               break;
+       }
+       for(i = 0; i < cnt; i++) {
+               do {
+                       x = nrand(SzX);
+                       y = nrand(SzY);
+               } while(ogrid[x][y] != 100);
+               ogrid[x][y] = 999;
+       }
+
+       ogrid[SzX/2][SzY/2] = 1000;
+
+       memcpy(grid, ogrid, sizeof grid);
+
+       finished = 0;
+
+}
+
+void
+drawlevel(void)
+{
+       Point p;
+       int  x, y, rx, ry, d;
+       char *s = nil;
+
+       if(finished)
+               draw(screen, screen->r, finished==Won?won:lost, nil, ZP);
+       else
+               draw(screen, screen->r, bg, nil, ZP);
+
+       d = (Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20: Dx(screen->r) -20;
+       rx = (int)ceil((float)(d-2*Border)/(float)SzX)/2;
+       ry = (int)ceil((float)(d-2*Border)/(float)SzY)/2;
+
+       for(x = 0; x < SzX; x++) {
+               for(y = 0; y < SzY; y++) {
+                       p = board2pix(x, y);
+                       switch(grid[x][y]){
+                       case 999: 
+                               fillellipse(screen, addpt(screen->r.min, p), rx, ry, cc, ZP);
+                               break;
+                       case 1000:
+                               p = addpt(screen->r.min, p);
+                               fillellipse(screen, p, rx, ry, ec, ZP);
+                               p = subpt(p, Pt(24, 24));
+                               draw(screen, Rpt(p, addpt(p, Pt(48, 48))), gl, glm, ZP);
+                               break;
+                       default:
+                               fillellipse(screen, addpt(screen->r.min, p), rx, ry, ec, ZP);
+                               USED(s);
+                               /* uncomment the following to see game state and field scores */
+                               /*s = smprint("%d", grid[x][y]);
+                               string(screen, addpt(screen->r.min, p), display->black, ZP, font, s);
+                               free(s);
+                               */
+                               break;
+                       }
+               }
+       }
+       flushimage(display, 1);
+}
+
+void
+domove(int dir, int x, int y)
+{
+       if(x == 0 || x == SzX-1 || y == 0 || y == SzY-1)
+               goto done;
+
+       switch(dir){
+       case NE:
+               if(y%2)
+                       grid[x+1][y-1] = 1000;
+               else    
+                       grid[x][y-1] = 1000;
+               break;
+       case E:
+               grid[x+1][y] = 1000;
+               break;
+       case SE:
+               if(y%2)
+                       grid[x+1][y+1] = 1000;
+               else
+                       grid[x][y+1] = 1000;
+               break;
+       case SW:
+               if(y%2)
+                       grid[x][y+1] = 1000;
+               else
+                       grid[x-1][y+1] = 1000;
+               break;
+       case W:
+               grid[x-1][y] = 1000;
+               break;
+       case NW:
+               if(y%2)
+                       grid[x][y-1] = 1000;
+               else
+                       grid[x-1][y-1] = 1000;
+               break;
+       }
+done:
+       grid[x][y] = 100;
+}
+
+Point
+findglenda(void)
+{
+       int x, y;
+       for(x = 0; x < SzX; x++)
+               for(y = 0; y < SzY; y++)
+                       if(grid[x][y] == 1000)
+                               return Pt(x, y);
+       return Pt(-1, -1);
+}
+
+int 
+checknext(int dir, int x, int y)
+{
+       switch(dir){
+       case NE: 
+               return grid[x+(y%2?1:0)][y-1];
+       case E:
+               return grid[x+1][y];
+       case SE:
+               return grid[x+(y%2?1:0)][y+1];
+       case SW:
+               return grid[x+(y%2?0:-1)][y+1];
+       case W:
+               return grid[x-1][y];
+       case NW:
+               return grid[x+(y%2?0:-1)][y-1];
+       default:
+               sysfatal("andrey messed up big time");
+       }
+       return 1000;
+}
+/* the following two routines constitute the "game AI"
+* they score the field based on the number of moves
+* required to reach the edge from a particular point
+* scores > 100 are "dead spots" (this assumes the field 
+* is not larger than ~100*2
+* 
+* routines need to run at least twice to ensure a field is properly
+* scored: there are errors that creep up due to the nature of 
+* traversing the board
+*/
+int 
+score1(int x, int y) {
+       int dir, min = 999, next;
+
+       if(x == 0 || x == SzX-1 || y == 0 || y == SzY-1)
+               return 1;               /* we can always escape from the edges */
+
+       for(dir = NE; dir <= NW; dir++) {
+               next = checknext(dir, x, y);
+               if(next < min)
+                       min = next;
+       }
+       return 1+min;
+}
+
+void
+calc(void)
+{
+       int i, x, y;
+       for(i = 0; i < SzX; i++) /* assumes SzX = SzY */
+               for(x = i; x < SzX-i; x++)
+                       for(y = i; y < SzY-i; y++)
+                               if(grid[x][y] != 999)
+                                       grid[x][y] = score1(x, y);
+}
+
+void
+nextglenda(void)
+{
+       int min =1000, next, dir, nextdir = 0, count = 0;
+       Point p = findglenda();
+
+       calc();
+       calc();
+       calc();
+
+       grid[p.x][p.y] = 1000;
+       
+       for(dir = NE; dir <= NW; dir++) {
+               next = checknext(dir, p.x, p.y);
+               if(next < min) {
+                       min = next;
+                       nextdir = dir;
+                       ++count;
+               } else if(next == min) {
+                       nextdir = (nrand(++count) == 0)?dir:nextdir;
+               }
+       }
+       if(min < 100)
+               domove(nextdir, p.x, p.y);
+       else
+               finished = Won;
+
+       if(eqpt(findglenda(), Pt(-1, -1)))
+               finished = Lost;
+}
+
+int
+checkfinished(void)
+{
+       int i, j;
+       for(i = 0; i < SzX; i++)
+               for(j = 0; j < SzY; j++)
+                       if(grid[i][j] == 'E')
+                               return 0;
+       return 1;
+}
+
+void
+move(Point m)
+{
+       Point p, nm;
+       int x, y;
+
+       nm = subpt(m, screen->r.min);
+
+       /* figure out where the click falls */
+       p = pix2board(nm.x, nm.y);
+       
+       if(grid[p.x][p.y] >= 999)
+               return;
+
+       /* reset the board scores */
+       grid[p.x][p.y] = 999;
+       for(x = 0; x < SzX; x++)
+               for(y = 0; y < SzY; y++)
+                       if(grid[x][y] != 999 && grid[x][y] != 1000)
+                               grid[x][y] = 100;
+       
+       nextglenda();
+}
+
+void
+resize(void)
+{
+       int fd, size = (Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) + 20 : Dx(screen->r)+20; 
+
+       fd = open("/dev/wctl", OWRITE);
+       if(fd >= 0){
+               fprint(fd, "resize -dx %d -dy %d", size, size);
+               close(fd);
+       }
+
+}
+
+
+void
+eresized(int new)
+{
+       if(new && getwindow(display, Refnone) < 0)
+               sysfatal("can't reattach to window");
+       
+       drawlevel();
+}
+
+void 
+main(int argc, char **argv)
+{
+       Mouse m;
+       Event ev;
+       int e, mousedown=0;
+       char *fontname;
+
+       USED(argv, argc);
+
+       if(initdraw(nil, nil, "glendy") < 0)
+               sysfatal("initdraw failed: %r");
+       einit(Emouse);
+
+       resize();
+
+       srand(time(0));
+
+       allocimages();
+       initlevel();    /* must happen before "eresized" */
+       eresized(0);
+
+       fontname = "/lib/font/bit/lucidasans/unicode.8.font";
+       if((font = openfont(display, fontname)) == nil)
+               sysfatal("font '%s' not found", fontname);      
+
+       for(;;) {
+               e = event(&ev);
+               switch(e) {
+               case Emouse:
+                       m = ev.mouse;
+                       if(m.buttons == 0) {
+                               if(mousedown && !finished) {
+                                       mousedown = 0;
+                                       move(m.xy);
+                                       drawlevel();
+                               }
+                       }
+                       if(m.buttons&1) {
+                               mousedown = 1;
+                       }
+                       if(m.buttons&2) {
+                               switch(emenuhit(2, &m, &mmenu)) {
+                               case 0:
+                                       difficulty = DEasy;
+                                       initlevel();
+                                       break;
+                               case 1:                         
+                                       difficulty = DMed;
+                                       initlevel();
+                                       break;
+                               case 2:
+                                       difficulty = DHard;
+                                       initlevel();
+                                       break;
+                               }
+                               drawlevel();
+                       }
+                       if(m.buttons&4) {
+                               switch(emenuhit(3, &m, &rmenu)) {
+                               case 0:
+                                       initlevel();
+                                       break;
+                               case 1:
+                                       memcpy(grid, ogrid, sizeof grid);
+                                       finished = 0;
+                                       break;
+                               case 2:
+                                       exits(nil);
+                               }
+                               drawlevel();
+                       }
+                       break;
+               }
+       }
+}
index 73467cc329697f69168df5917e66ade1b1dfb8ee..e59b1ad7f6df7645794079f913e5114dfbabe082 100644 (file)
@@ -9,6 +9,7 @@ TARG=4s\
        life\
        memo\
        mole\
+       glendy\
 
 OFILES=
 HFILES=