7 /* difficulty levels (how many circles are initially occupied) */
12 /* dynamic? original game has a fixed grid size, but we don't need to abide by it */
17 /* movement directions */
25 Won = 1, /* game-ending states */
31 int difficulty = DMed;
35 int ogrid[SzX][SzY]; /* so we can restart levels */
37 Image *gl; /* glenda */
38 Image *glm; /* glenda's mask */
39 Image *cc; /* clicked */
40 Image *ec; /* empty; not clicked */
73 eallocimage(Rectangle r, int repl, uint color)
77 tmp = allocimage(display, r, screen->chan, repl, color);
79 sysfatal("cannot allocate buffer image: %r");
90 fd = open(path, OREAD);
92 fprint(2, "cannot open image file %s: %r\n", path);
95 img = readimage(display, fd, 0);
97 sysfatal("cannot load image: %r");
107 Rectangle one = Rect(0, 0, 1, 1);
109 cc = eallocimage(one, 1, 0x777777FF);
110 ec = eallocimage(one, 1, DPalegreen);
111 bg = eallocimage(one, 1, DPurpleblue);
112 lost = eallocimage(one, 1, DRed);
113 won = eallocimage(one, 1, DGreen);
114 gl = eloadfile("/lib/face/48x48x4/g/glenda.1");
116 glm = allocimage(display, Rect(0, 0, 48, 48), gl->chan, 1, DCyan);
118 sysfatal("cannot allocate mask: %r");
120 draw(glm, glm->r, display->white, nil, ZP);
121 gendraw(glm, glm->r, display->black, ZP, gl, gl->r.min);
128 /* unnecessary calculations here, but it's fine */
130 board2pix(int x, int y)
135 d = (float)(Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20 : Dx(screen->r) -20;
143 nx = (int)((float)x*rx*2.0+rx +(y%2?rx:0.0)); /* nx = x*(2rx) + rx + rx (conditional) */
144 ny = (int)((float)y*(ry*2.0-(y>0?yh:0.0)) + ry); /* ny = y*(2ry-yh) +ry */
149 pix2board(int x, int y)
154 /* XXX: float→int causes small rounding errors */
156 d = (float)(Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20: Dx(screen->r)-20;
164 /* reverse board2pix() */
165 ny = (int)(((float)y - ry)/(2*ry - ((y>2*ry)?yh:0.0)) + 0.5); /* ny = (y - ry)/(2ry-yh) */
166 nx = (int)(((float)x - rx - (ny%2?rx:0.0))/(rx*2.0) + 0.5); /* nx = (x - rx - rx)/2rx */
179 int i, cnt = 10, x, y;
181 for(x = 0; x < SzX; x++)
182 for(y = 0; y < SzY; y++)
196 for(i = 0; i < cnt; i++) {
200 } while(ogrid[x][y] != 100);
204 ogrid[SzX/2][SzY/2] = 1000;
206 memcpy(grid, ogrid, sizeof grid);
220 draw(screen, screen->r, finished==Won?won:lost, nil, ZP);
222 draw(screen, screen->r, bg, nil, ZP);
224 d = (Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20: Dx(screen->r) -20;
225 rx = (int)ceil((float)(d-2*Border)/(float)SzX)/2;
226 ry = (int)ceil((float)(d-2*Border)/(float)SzY)/2;
228 for(x = 0; x < SzX; x++) {
229 for(y = 0; y < SzY; y++) {
233 fillellipse(screen, addpt(screen->r.min, p), rx, ry, cc, ZP);
236 p = addpt(screen->r.min, p);
237 fillellipse(screen, p, rx, ry, ec, ZP);
238 p = subpt(p, Pt(24, 24));
239 draw(screen, Rpt(p, addpt(p, Pt(48, 48))), gl, glm, ZP);
242 fillellipse(screen, addpt(screen->r.min, p), rx, ry, ec, ZP);
244 /* uncomment the following to see game state and field scores */
245 /*s = smprint("%d", grid[x][y]);
246 string(screen, addpt(screen->r.min, p), display->black, ZP, font, s);
253 flushimage(display, 1);
257 domove(int dir, int x, int y)
259 if(x == 0 || x == SzX-1 || y == 0 || y == SzY-1)
265 grid[x+1][y-1] = 1000;
274 grid[x+1][y+1] = 1000;
282 grid[x-1][y+1] = 1000;
291 grid[x-1][y-1] = 1000;
302 for(x = 0; x < SzX; x++)
303 for(y = 0; y < SzY; y++)
304 if(grid[x][y] == 1000)
310 checknext(int dir, int x, int y)
314 return grid[x+(y%2?1:0)][y-1];
318 return grid[x+(y%2?1:0)][y+1];
320 return grid[x+(y%2?0:-1)][y+1];
324 return grid[x+(y%2?0:-1)][y-1];
326 sysfatal("andrey messed up big time");
330 /* the following two routines constitute the "game AI"
331 * they score the field based on the number of moves
332 * required to reach the edge from a particular point
333 * scores > 100 are "dead spots" (this assumes the field
334 * is not larger than ~100*2
336 * routines need to run at least twice to ensure a field is properly
337 * scored: there are errors that creep up due to the nature of
338 * traversing the board
341 score1(int x, int y) {
342 int dir, min = 999, next;
344 if(x == 0 || x == SzX-1 || y == 0 || y == SzY-1)
345 return 1; /* we can always escape from the edges */
347 for(dir = NE; dir <= NW; dir++) {
348 next = checknext(dir, x, y);
352 if(min == 999) return 998;
360 for(i = 0; i < SzX; i++) /* assumes SzX = SzY */
361 for(x = i; x < SzX-i; x++)
362 for(y = i; y < SzY-i; y++)
363 if(grid[x][y] != 999)
364 grid[x][y] = score1(x, y);
370 int min =1000, next, dir, nextdir = 0, count = 0;
371 Point p = findglenda();
377 grid[p.x][p.y] = 1000;
379 for(dir = NE; dir <= NW; dir++) {
380 next = checknext(dir, p.x, p.y);
385 } else if(next == min) {
386 nextdir = (nrand(++count) == 0)?dir:nextdir;
390 domove(nextdir, p.x, p.y);
394 if(eqpt(findglenda(), Pt(-1, -1)))
402 for(i = 0; i < SzX; i++)
403 for(j = 0; j < SzY; j++)
404 if(grid[i][j] == 'E')
415 nm = subpt(m, screen->r.min);
417 /* figure out where the click falls */
418 p = pix2board(nm.x, nm.y);
420 if(grid[p.x][p.y] >= 999)
423 /* reset the board scores */
424 grid[p.x][p.y] = 999;
425 for(x = 0; x < SzX; x++)
426 for(y = 0; y < SzY; y++)
427 if(grid[x][y] != 999 && grid[x][y] != 1000)
436 int fd, size = (Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) + 20 : Dx(screen->r)+20;
438 fd = open("/dev/wctl", OWRITE);
440 fprint(fd, "resize -dx %d -dy %d", size, size);
450 if(new && getwindow(display, Refnone) < 0)
451 sysfatal("can't reattach to window");
457 main(int argc, char **argv)
466 if(initdraw(nil, nil, "glendy") < 0)
467 sysfatal("initdraw failed: %r");
475 initlevel(); /* must happen before "eresized" */
478 fontname = "/lib/font/bit/lucidasans/unicode.8.font";
479 if((font = openfont(display, fontname)) == nil)
480 sysfatal("font '%s' not found", fontname);
488 if(mousedown && !finished) {
498 switch(emenuhit(2, &m, &mmenu)) {
515 switch(emenuhit(3, &m, &rmenu)) {
520 memcpy(grid, ogrid, sizeof grid);