ref: 7ca11774a1a78dcfa520f35a60d6ce34c2dc724a
parent: b7d31909f6258a36d00cb2bc2ef08687d6817755
author: aiju <devnull@localhost>
date: Sat Mar 10 09:40:34 EST 2018
games/mines: build a better ghost trap
--- a/sys/src/games/mines/fns.h
+++ b/sys/src/games/mines/fns.h
@@ -2,5 +2,7 @@
void RightClick(Point);
void DrawButton(int);
void InitMineField(void);
+void GhostInit(void);
void GhostMode(void);
-void GhostReset(void);
+void GhostReset(int);
+int GhostCheck(Point);
--- a/sys/src/games/mines/ghost.c
+++ b/sys/src/games/mines/ghost.c
@@ -2,9 +2,18 @@
#include <libc.h>
#include <draw.h>
#include <event.h>
+#include <mp.h>
+#include <cursor.h>
#include "dat.h"
#include "fns.h"
+enum { TURBO = 0 };
+
+static Cursor bunny = {
+ .set = {0x0,0x32,0x60,0x76,0x40,0x6e,0x3,0xec,0xf,0xfc,0x1f,0xf8,0x27,0x9c,0x3,0xc,0x33,0xcc,0x73,0xce,0x67,0x9e,0x7f,0xfe,0x7f,0xfe,0x7f,0xfe,0x6e,0x76},
+ .clr = {0xf8,0xcd,0x90,0x89,0xa3,0x91,0xcc,0x12,0x90,0x2,0x20,0x6,0x58,0x62,0x7c,0xf2,0x4c,0x33,0x8c,0x31,0x98,0x61,0x80,0x1,0x80,0x1,0x80,0x1,0x91,0x89}
+};
+
enum {
MEmpty,
MMine,
@@ -12,39 +21,71 @@
NState,
};
-int ghostactive;
-int ghostwait;
-Point ghosttarget;
+typedef struct Ghost Ghost;
+typedef struct CList CList;
+typedef struct CGroup CGroup;
+struct Ghost {
+ QLock;
+ Rendez;
+ int active, wait;
+ int pid;
+
+ enum { NOTARG, LTARG, RTARG } targettype;
+ Point target;
+ int generation;
+
+ int nmines;
+ uchar *f;
+
+ int w, h;
+
+ int cursor;
+ Point moves[3];
+ int nmoves;
+ Point cpos;
+};
+
+struct CList {
+ int mines, pts;
+ int pt[8];
+ int next;
+};
+
+struct CGroup {
+ CList *cl;
+ int ncl;
+ mpint **cnt;
+ int max;
+ int cur;
+};
+
+static Ghost ghost;
+
static uchar ***
-neighbours(uchar **f, uchar ***n)
+neighbours(void)
{
int x, y, p;
+ uchar ***n;
- if(n != nil){
- for(x = 0; x < MaxX; x++)
- for(y = 0; y < MaxY; y++)
- memset(n[x][y], 0, NState);
- }else{
- n = calloc(sizeof(void*), MaxX);
- for(x = 0; x < MaxX; x++){
- n[x] = calloc(sizeof(void*), MaxY);
- for(y = 0; y < MaxY; y++)
- n[x][y] = calloc(sizeof(uchar), NState);
- }
+ n = calloc(sizeof(void*), ghost.w);
+ for(x = 0; x < ghost.w; x++){
+ n[x] = calloc(sizeof(void*), ghost.h);
+ for(y = 0; y < ghost.h; y++)
+ n[x][y] = calloc(sizeof(uchar), NState);
}
- for(y = 0; y < MaxY; y++)
- for(x = 0; x < MaxX; x++){
- p = f[x][y];
+ for(y = 0; y < ghost.h; y++)
+ for(x = 0; x < ghost.w; x++){
+ p = ghost.f[x + y * ghost.w];
if(x > 0 && y > 0) n[x-1][y-1][p]++;
if(y > 0) n[x][y-1][p]++;
- if(x < MaxX-1 && y > 0) n[x+1][y-1][p]++;
+ if(x < ghost.w-1 && y > 0) n[x+1][y-1][p]++;
if(x > 0) n[x-1][y][p]++;
- if(x < MaxX-1) n[x+1][y][p]++;
- if(x > 0 && y < MaxY-1) n[x-1][y+1][p]++;
- if(y < MaxY-1) n[x][y+1][p]++;
- if(x < MaxX-1 && y < MaxY-1) n[x+1][y+1][p]++;
+ if(x < ghost.w-1) n[x+1][y][p]++;
+ if(x > 0 && y < ghost.h-1) n[x-1][y+1][p]++;
+ if(y < ghost.h-1) n[x][y+1][p]++;
+ if(x < ghost.w-1 && y < ghost.h-1) n[x+1][y+1][p]++;
}
return n;
}
@@ -53,11 +94,9 @@
freeneighbours(uchar ***n)
{
int x, y;
-
- if(n == nil)
- return;
- for(x = 0; x < MaxX; x++){
- for(y = 0; y < MaxY; y++)
+
+ for(x = 0; x < ghost.w; x++){
+ for(y = 0; y < ghost.h; y++)
free(n[x][y]);
free(n[x]);
}
@@ -64,57 +103,53 @@
free(n);
}
-static int
-allneighbours(uchar **f, int x, int y, int (*fun)(uchar **, int, int, void *), void *aux)
+static void
+printcl(CList *cl, int ncl)
{
- int rc;
-
- rc = 0;
- if(x > 0 && y > 0) rc += fun(f, x-1, y-1, aux);
- if(y > 0) rc += fun(f, x, y-1, aux);
- if(x < MaxX-1 && y > 0) rc += fun(f, x+1, y-1, aux);
- if(x > 0) rc += fun(f, x-1, y, aux);
- if(x < MaxX-1) rc += fun(f, x+1, y, aux);
- if(x > 0 && y < MaxY-1) rc += fun(f, x-1, y+1, aux);
- if(y < MaxY-1) rc += fun(f, x, y+1, aux);
- if(x < MaxX-1 && y < MaxY-1) rc += fun(f, x+1, y+1, aux);
- return rc;
-}
-
-typedef struct {
- int mines, pts;
- Point pt[8];
-} CList;
-
-static int
-addlist(uchar **f, int x, int y, void *aux)
-{
- CList *c;
+ int i, j;
- c = aux;
- if(f[x][y] == MUnknown)
- c->pt[c->pts++] = (Point){x,y};
- return 0;
+ for(i = 0; i < ncl; i++){
+ print("%d: ", cl[i].mines);
+ for(j = 0; j < cl[i].pts; j++)
+ print("%d ", cl[i].pt[j]);
+ print("\n");
+ }
+ print("--\n");
}
static void
-mklists(uchar **f, CList **clp, int *nclp)
+mklists(CList **clp, int *nclp)
{
- CList *cl;
+ CList *c, *cl;
int ncl, x, y;
uchar ***nei;
cl = nil;
ncl = 0;
- nei = neighbours(f, nil);
- for(y = 0; y < MaxY; y++)
- for(x = 0; x < MaxX; x++)
+ nei = neighbours();
+ for(y = 0; y < ghost.h; y++)
+ for(x = 0; x < ghost.w; x++)
if(MineField[x][y].Picture <= Empty8 && nei[x][y][MUnknown] > 0){
cl = realloc(cl, (ncl + 1) * sizeof(CList));
- memset(&cl[ncl], 0, sizeof(CList));
- cl[ncl].mines = MineField[x][y].Picture - nei[x][y][MMine];
- allneighbours(f, x, y, addlist, &cl[ncl]);
- ncl++;
+ c = &cl[ncl++];
+ memset(c, 0, sizeof(CList));
+ c->mines = MineField[x][y].Picture - nei[x][y][MMine];
+ if(x > 0 && y > 0 && ghost.f[(x-1)+(y-1)*ghost.w] == MUnknown)
+ c->pt[c->pts++] = (x-1)+(y-1)*ghost.w;
+ if(y > 0 && ghost.f[(x)+(y-1)*ghost.w] == MUnknown)
+ c->pt[c->pts++] = (x)+(y-1)*ghost.w;
+ if(x < ghost.w-1 && y > 0 && ghost.f[(x+1)+(y-1)*ghost.w] == MUnknown)
+ c->pt[c->pts++] = (x+1)+(y-1)*ghost.w;
+ if(x > 0 && ghost.f[(x-1)+(y)*ghost.w] == MUnknown)
+ c->pt[c->pts++] = (x-1)+(y)*ghost.w;
+ if(x < ghost.w-1 && ghost.f[(x+1)+(y)*ghost.w] == MUnknown)
+ c->pt[c->pts++] = (x+1)+(y)*ghost.w;
+ if(x > 0 && y < ghost.h-1 && ghost.f[(x-1)+(y+1)*ghost.w] == MUnknown)
+ c->pt[c->pts++] = (x-1)+(y+1)*ghost.w;
+ if(y < ghost.h-1 && ghost.f[(x)+(y+1)*ghost.w] == MUnknown)
+ c->pt[c->pts++] = (x)+(y+1)*ghost.w;
+ if(x < ghost.w-1 && y < ghost.h-1 && ghost.f[(x+1)+(y+1)*ghost.w] == MUnknown)
+ c->pt[c->pts++] = (x+1)+(y+1)*ghost.w;
}
freeneighbours(nei);
*clp = cl;
@@ -122,56 +157,467 @@
}
static int
-ismember(CList *c, Point p)
+ismember(CList *c, int p)
{
int i;
for(i = 0; i < c->pts; i++)
- if(c->pt[i].x == p.x && c->pt[i].y == p.y)
+ if(c->pt[i] == p)
return 1;
return 0;
}
static void
-merge(CList *cl, int *nclp)
+splitknown(CList **clp, int *nclp, int i, int *lastq)
{
- int i, j, k, l;
+ int j;
+ CList *cl;
+ int ncl;
+
+ cl = *clp;
+ ncl = *nclp;
+ cl = realloc(cl, sizeof(CList) * (ncl + cl[i].pts - 1));
+ memset(cl + ncl, 0, sizeof(CList) * (cl[i].pts - 1));
+ for(j = 1; j < cl[i].pts; j++){
+ cl[ncl - 1 + j].mines = cl[i].pts == cl[i].mines;
+ cl[ncl - 1 + j].pts = 1;
+ cl[ncl - 1 + j].pt[0] = cl[i].pt[j];
+ cl[*lastq].next = i;
+ *lastq = i;
+ }
+ cl[i].mines = cl[i].pts == cl[i].mines;
+ *nclp += cl[i].pts - 1;
+ cl[i].pts = 1;
+ *clp = cl;
+}
-start:
+static int
+merge(CList **clp, int *nclp, int start, int split)
+{
+ int i, j, k, l, p;
+ CList *cl, *q, *r;
+ int qi, lastq;
+ int zero;
+
+ cl = *clp;
+ qi = -1;
+ lastq = -1;
+ zero = 0;
for(i = 0; i < *nclp; i++)
- for(j = 0; j < *nclp; j++){
- if(i == j) continue;
- for(k = 0; k < cl[i].pts; k++)
- if(!ismember(&cl[j], cl[i].pt[k]))
- goto next;
- for(k = l = 0; k < cl[j].pts; k++)
- if(!ismember(&cl[i], cl[j].pt[k]))
- cl[j].pt[l++] = cl[j].pt[k];
- cl[j].pts = l;
- cl[j].mines -= cl[i].mines;
- if(l == 0){
- memcpy(&cl[j], &cl[j+1], (*nclp - j - 1) * sizeof(CList));
- (*nclp)--;
+ if(cl[i].pts == 0 || start >= 0 && start != i)
+ cl[i].next = -2;
+ else{
+ cl[i].next = -1;
+ if(lastq >= 0)
+ cl[lastq].next = i;
+ else
+ qi = i;
+ lastq = i;
+ }
+ while(qi >= 0){
+ q = &cl[qi];
+ for(i = 0; i < *nclp; i++){
+ r = &cl[i];
+ if(r == q || r->pts < q->pts) continue;
+ for(j = k = 0; j < q->pts; j++, k++){
+ p = q->pt[j];
+ if(r->pt[r->pts - 1] < p)
+ goto next;
+ while(r->pt[k] < p)
+ k++;
+ if(r->pt[k] > p)
+ goto next;
}
- goto start;
+ for(j = k = l = 0; j < q->pts; j++, k++){
+ p = q->pt[j];
+ while(r->pt[k] < p)
+ r->pt[l++] = r->pt[k++];
+ }
+ while(k < r->pts)
+ r->pt[l++] = r->pt[k++];
+ r->pts = l;
+ r->mines -= q->mines;
+ if(r->mines < 0 || r->mines > r->pts){
+ *clp = cl;
+ return -1;
+ }
+ if(r->pts == 0 && r->mines == 0)
+ zero++;
+ if(r->next == -2 && r->pts > 0){
+ r->next = -1;
+ cl[lastq].next = i;
+ lastq = i;
+ }
+ if(split && r->pts > 1 && (r->pts == r->mines || r->mines == 0)){
+ splitknown(&cl, nclp, i, &lastq);
+ q = &cl[qi];
+ }
next: ;
- }
+ }
+ qi = q->next;
+ q->next = -1;
+ }
+ if(zero != 0){
+ for(i = 0, j = 0; i < *nclp; i++)
+ if(cl[i].mines != 0 || cl[i].pts != 0)
+ cl[j++] = cl[i];
+ *nclp = j;
+ }
+ *clp = cl;
+ return 0;
}
static void
-ghostfind(void)
+countref(CList *cl, int ncl, uchar *ref)
{
- int x, y, i, j, n;
- uchar **field;
- CList *cl;
- int ncl;
- Point pd;
- int d, min;
+ int i, j;
+
+ memset(ref, 0, ghost.w * ghost.h);
+ for(i = 0; i < ncl; i++)
+ for(j = 0; j < cl[i].pts; j++)
+ ref[cl[i].pt[j]]++;
+}
- field = calloc(sizeof(uchar*), MaxX);
- for(x = 0; x < MaxX; x++){
- field[x] = calloc(sizeof(uchar), MaxY);
- for(y = 0; y < MaxY; y++)
+static int *
+clgroup(CList *cl, int ncl, uchar *ref)
+{
+ int i, j, k, og, ng;
+ int *g;
+
+ g = calloc(ncl, sizeof(int));
+ for(i = 0; i < ncl; i++)
+ g[i] = i;
+start:
+ for(i = 0; i < ncl; i++)
+ for(j = 0; j < cl[i].pts; j++){
+ if(ref[cl[i].pt[j]] == 1) continue;
+ for(k = 0; k < ncl; k++)
+ if(g[i] != g[k] && ismember(&cl[k], cl[i].pt[j]))
+ break;
+ if(k == ncl) continue;
+ if(g[i] < g[k]){
+ og = g[i];
+ ng = g[k];
+ }else{
+ og = g[k];
+ ng = g[i];
+ }
+ for(k = 0; k < ncl; k++)
+ if(g[k] == og)
+ g[k] = ng;
+ goto start;
+ }
+ return g;
+}
+
+static int
+groupmin(int *g, int ncl)
+{
+ int i, j, og, ng;
+ u32int *bs;
+
+ bs = calloc(sizeof(u32int), ncl + 31 >> 5);
+ for(i = 0; i < ncl; i++)
+ bs[g[i]>>5] |= 1<<(g[i] & 31);
+ for(i = 0; i < ncl; i++){
+ for(j = 0; j < g[i]; j++)
+ if((bs[j>>5] & 1<<(j & 31)) == 0)
+ break;
+ if(j == g[i]) continue;
+ og = g[i];
+ ng = j;
+ for(j = 0; j < ncl; j++)
+ if(g[j] == og)
+ g[j] = ng;
+ bs[og>>5] &= ~(1<<(og & 31));
+ bs[ng>>5] |= 1<<(ng & 31);
+ }
+ og = 0;
+ for(i = 0; i < ncl; i++)
+ if(g[i] + 1 > og)
+ og = g[i] + 1;
+ free(bs);
+ return og;
+}
+
+static int
+mkgroups(CList *cl, int ncl, uchar *ref, CGroup **gp)
+{
+ CGroup *g, *p;
+ int i, j, k, ng;
+ int *gr;
+
+ gr = clgroup(cl, ncl, ref);
+ ng = groupmin(gr, ncl);
+ g = calloc(sizeof(CGroup), ng);
+ for(i = 0; i < ng; i++){
+ p = &g[i];
+ for(j = 0; j < ncl; j++)
+ if(gr[j] == i)
+ p->ncl++;
+ p->cl = calloc(sizeof(CList), p->ncl);
+ for(j = 0, k = 0; j < ncl; j++)
+ if(gr[j] == i)
+ p->cl[k++] = cl[j];
+ }
+ free(gr);
+ *gp = g;
+ return ng;
+}
+
+static void
+freegroups(CGroup *g, int ng)
+{
+ int i, j;
+
+ for(i = 0; i < ng; i++){
+ for(j = 0; j <= g[i].max; j++)
+ mpfree(g[i].cnt[j]);
+ free(g[i].cl);
+ free(g[i].cnt);
+ }
+ free(g);
+}
+
+static void
+binom(mpint *acc, mpint *temp, int n, int k)
+{
+ int i;
+
+ for(i = 0; i < k; i++){
+ itomp(n - i, temp);
+ mpmul(acc, temp, acc);
+ }
+ for(i = 2; i <= k; i++){
+ itomp(i, temp);
+ mpdiv(acc, temp, acc, nil);
+ }
+}
+
+static void countcomb(CList *cl, int ncl, mpint **);
+
+static void
+cltry(CList *cl, int ncl, int p, int v, mpint **cnt)
+{
+ CList *cm;
+ int ncm;
+
+ cm = calloc(ncl + 1, sizeof(CList));
+ memcpy(cm, cl, ncl * sizeof(CList));
+ memset(&cm[ncl], 0, sizeof(CList));
+ cm[ncl].mines = v;
+ cm[ncl].pts = 1;
+ cm[ncl].pt[0] = p;
+ ncm = ncl + 1;
+ if(merge(&cm, &ncm, ncl, 1) < 0){
+ free(cm);
+ return;
+ }
+ countcomb(cm, ncm, cnt);
+ free(cm);
+}
+
+static void
+countcomb(CList *cl, int ncl, mpint **cnt)
+{
+ uchar *ref;
+ int p, i, j, nm;
+ mpint *rc, *c0;
+
+ ref = calloc(ghost.w, ghost.h);
+ countref(cl, ncl, ref);
+ for(i = 0; i < ncl; i++)
+ for(j = 0; j < cl[i].pts; j++){
+ p = cl[i].pt[j];
+ if(ref[p] > 1){
+ cltry(cl, ncl, p, 0, cnt);
+ cltry(cl, ncl, p, 1, cnt);
+ free(ref);
+ return;
+ }
+ }
+ rc = itomp(1, nil);
+ c0 = mpnew(0);
+ nm = 0;
+ for(i = 0; i < ncl; i++){
+ nm += cl[i].mines;
+ binom(rc, c0, cl[i].pts, cl[i].mines);
+ }
+ if(mpcmp(rc, mpzero) != 0)
+ if(cnt[nm] == nil)
+ cnt[nm] = rc;
+ else{
+ mpadd(cnt[nm], rc, cnt[nm]);
+ mpfree(rc);
+ }
+ else
+ mpfree(rc);
+ mpfree(c0);
+ free(ref);
+}
+
+static mpint *
+totcomb(int tot, int nf, CGroup *g, int ng)
+{
+ int i, j, s, carry;
+ mpint *r, *v, *t, *m;
+
+ s = 0;
+ for(i = 0; i < ng; i++){
+ for(j = 0; j <= g[i].max; j++)
+ if(g[i].cnt[j] != nil)
+ break;
+ if(j > g[i].max)
+ return mpnew(0);
+ g[i].cur = j;
+ s += j;
+ }
+ if(s > tot)
+ return mpnew(0);
+ r = mpnew(0);
+ v = mpnew(0);
+ t = mpnew(0);
+ do{
+ itomp(1, v);
+ s = 0;
+ for(i = 0; i < ng; i++){
+ m = g[i].cnt[g[i].cur];
+ mpmul(v, m, v);
+ s += g[i].cur;
+ }
+ if(s <= tot){
+ binom(v, t, nf, tot - s);
+ mpadd(r, v, r);
+ }
+ for(i = 0; i < ng; i++){
+ carry = 0;
+ do
+ if(++g[i].cur > g[i].max){
+ g[i].cur = 0;
+ carry = 1;
+ }
+ while(g[i].cnt[g[i].cur] == nil);
+ if(!carry) break;
+ }
+ }while(i < ng);
+ mpfree(v);
+ mpfree(t);
+ return r;
+}
+
+static int
+freefields(uchar *ref)
+{
+ int nf, p;
+
+ nf = 0;
+ for(p = 0; p < ghost.w * ghost.h; p++)
+ if(ref[p] == 0 && ghost.f[p] == MUnknown)
+ nf++;
+ return nf;
+}
+
+static int
+basecounts(CGroup *g, int ng)
+{
+ int i, j;
+ int allmax;
+ CGroup *p;
+
+ allmax = 0;
+ for(i = 0; i < ng; i++){
+ p = &g[i];
+ p->max = 0;
+ for(j = 0; j < p->ncl; j++)
+ p->max += p->cl[j].mines;
+ p->cnt = calloc(p->max + 1, sizeof(mpint *));
+ countcomb(p->cl, p->ncl, p->cnt);
+ if(p->max > allmax) allmax = p->max;
+ }
+ return allmax;
+}
+
+static void
+fieldcombs(CList *cl, int ncl, int *xp, int *yp)
+{
+ uchar *ref;
+ int i, k, p;
+ int nf, ng;
+ int allmax;
+ int ref1;
+ mpint *min, *v, **alt, **backup;
+ CList *l;
+ CGroup *g, *gp;
+
+ ref = calloc(ghost.w, ghost.h);
+ countref(cl, ncl, ref);
+ nf = freefields(ref);
+ ng = mkgroups(cl, ncl, ref, &g);
+ allmax = basecounts(g, ng);
+ alt = calloc(allmax + 1, sizeof(mpint *));
+
+ *xp = -1;
+ *yp = -1;
+ min = mpnew(0);
+
+ for(gp = g; gp < g + ng; gp++)
+ for(l = gp->cl; l < gp->cl + gp->ncl; l++){
+ ref1 = 0;
+ for(i = 0; i < l->pts; i++){
+ p = l->pt[i];
+ if(ref[p] == 0xff)
+ continue;
+ if(ref[p] == 1 && ref1++ != 0)
+ continue;
+ ref[p] = 0xff;
+
+ cltry(gp->cl, gp->ncl, p, 1, alt);
+ backup = gp->cnt;
+ gp->cnt = alt;
+ v = totcomb(ghost.nmines, nf, g, ng);
+ if(*xp < 0 || mpcmp(v, min) < 0){
+ mpassign(v, min);
+ *xp = p % ghost.w;
+ *yp = p / ghost.w;
+ }
+
+ gp->cnt = backup;
+ for(k = 0; k <= gp->max; k++)
+ mpfree(alt[k]);
+ memset(alt, 0, (gp->max + 1) * sizeof(mpint *));
+ mpfree(v);
+ }
+ }
+ if(nf > 0){
+ v = totcomb(ghost.nmines - 1, nf - 1, g, ng);
+ if(*xp < 0 || mpcmp(v, min) < 0){
+ i = nrand(nf);
+ for(p = 0; p < ghost.w * ghost.h; p++)
+ if(ref[p] == 0 && ghost.f[p] == MUnknown && i-- == 0)
+ break;
+ mpassign(v, min);
+ *xp = p % ghost.w;
+ *yp = p / ghost.w;
+ }
+ mpfree(v);
+ }
+ mpfree(min);
+ free(alt);
+ free(ref);
+ freegroups(g, ng);
+}
+
+static void
+ghostprep(CList **clp, int *nclp)
+{
+ int x, y;
+
+ ghost.w = MaxX;
+ ghost.h = MaxY;
+ ghost.nmines = MinesRemain;
+ ghost.f = calloc(ghost.w, ghost.h);
+ for(x = 0; x < ghost.w; x++){
+ for(y = 0; y < ghost.h; y++)
switch(MineField[x][y].Picture){
case Empty0:
case Empty1:
@@ -182,102 +628,201 @@
case Empty6:
case Empty7:
case Empty8:
- field[x][y] = MEmpty;
+ ghost.f[x + y * ghost.w] = MEmpty;
break;
case Mark:
- field[x][y] = MMine;
+ ghost.f[x + y * ghost.w] = MMine;
break;
default:
- field[x][y] = MUnknown;
+ ghost.f[x + y * ghost.w] = MUnknown;
}
}
+ mklists(clp, nclp);
+}
+
+static int
+ghostfind(CList *cl, int ncl, Point *targp)
+{
+ int i, j, x, y;
+ Point p, pd;
+ int d, min;
+ Point targ;
+ int rt;
- mklists(field, &cl, &ncl);
- merge(cl, &ncl);
- ghostactive = -1;
+ merge(&cl, &ncl, -1, 0);
min = 0;
+ rt = NOTARG;
+ targ = ghost.cpos;
for(i = 0; i < ncl; i++)
if(cl[i].mines == 0 || cl[i].mines == cl[i].pts)
for(j = 0; j < cl[i].pts; j++){
- pd = subpt(addpt(addpt(mulpt(cl[i].pt[j], 16), Pt(12+8, 57+8)), Origin), LastMouse.xy);
+ p = Pt(cl[i].pt[j]%ghost.w, cl[i].pt[j]/ghost.w);
+ pd = subpt(addpt(addpt(mulpt(p, 16), Pt(12+8, 57+8)), Origin), targ);
d = pd.x * pd.x + pd.y * pd.y;
- if(ghostactive < 0 || d < min){
- ghostactive = 1 + (cl[i].mines == cl[i].pts);
- ghosttarget = cl[i].pt[j];
+ if(rt == NOTARG || d < min){
+ rt = cl[i].mines == cl[i].pts ? RTARG : LTARG;
+ *targp = p;
min = d;
}
- field[cl[i].pt[j].x][cl[i].pt[j].y] = cl[i].mines == cl[i].pts ? MMine : MEmpty;
+ ghost.f[cl[i].pt[j]] = cl[i].mines == cl[i].pts ? MMine : MEmpty;
}
- if(ghostactive < 0){
- n = 0;
- for(x = 0; x < MaxX; x++)
- for(y = 0; y < MaxY; y++)
- if(field[x][y] == MUnknown)
- n++;
- if(n == 0) goto done;
- n = lrand() % n;
- for(x = 0; x < MaxX; x++)
- for(y = 0; y < MaxY; y++)
- if(field[x][y] == MUnknown && n-- == 0){
- ghostactive = 1;
- ghosttarget = Pt(x, y);
- goto done;
- }
- done:;
+ if(rt == NOTARG){
+ fieldcombs(cl, ncl, &x, &y);
+ if(x >= 0){
+ rt = LTARG;
+ *targp = Pt(x, y);
+ }
}
- for(x = 0; x < MaxX; x++)
- free(field[x]);
- free(field);
+ free(ghost.f);
free(cl);
+ return rt;
}
+static void
+killchild(void)
+{
+ postnote(PNPROC, ghost.pid, "kill");
+}
+
void
-GhostMode(void)
+GhostInit(void)
{
- Point p, q;
+ CList *cl;
+ int ncl;
+ int rt;
+ Point p;
+ int rc;
+ int g;
+
+ ghost.Rendez.l = &ghost;
+ if(rc = rfork(RFPROC|RFMEM), rc == 0){
+ qlock(&ghost);
+ for(;;){
+ while(!ghost.active || ghost.targettype != NOTARG)
+ rsleep(&ghost);
+ g = ghost.generation;
+ ghostprep(&cl, &ncl);
+ qunlock(&ghost);
+ rt = ghostfind(cl, ncl, &p);
+ qlock(&ghost);
+ if(ghost.generation == g){
+ ghost.targettype = rt;
+ ghost.target = p;
+ }
+ }
+ }else{
+ ghost.pid = rc;
+ atexit(killchild);
+ }
+}
+
+int
+GhostCheck(Point p)
+{
+ int i;
+
+ for(i = 0; i < ghost.nmoves; i++)
+ if(eqpt(ghost.moves[i], p))
+ return 1;
+ return 0;
+}
+
+static void
+move(Point p)
+{
+ if(ghost.nmoves == nelem(ghost.moves))
+ memmove(ghost.moves + 1, ghost.moves, sizeof(ghost.moves) - sizeof(Point));
+ else
+ memmove(ghost.moves + 1, ghost.moves, ghost.nmoves++ * sizeof(Point));
+ ghost.moves[0] = p;
+ ghost.cpos = p;
+ emoveto(p);
+}
+
+static void
+approach(Point p)
+{
+ Point q;
double d;
- if(ghostwait > 0){
- ghostwait--;
+ q = subpt(p, ghost.cpos);
+ d = hypot(q.x, q.y);
+ if(d == 0)
return;
+ d = 2 / d * 2 * (1 + d / (400 + d));
+ move(addpt(ghost.cpos, Pt(ceil(q.x * d), ceil(q.y * d))));
+}
+
+void
+GhostMode(void)
+{
+ Point p;
+
+ if(!ghost.cursor){
+ esetcursor(&bunny);
+ ghost.cursor = 1;
}
+ if(ghost.wait > 0){
+ if(Status == Oops)
+ move(addpt(ghost.cpos, Pt(nrand(20) - 10, nrand(20) - 10)));
+ ghost.wait--;
+ return;
+ }
if(Status != Game){
- ghostactive = 0;
- p = Pt(Origin.x + MaxX * 8 + 12, Origin.y + 28);
- if(ptinrect(LastMouse.xy, insetrect(Rpt(p, p), -4))){
+ ghost.active = 0;
+ p = Pt(Origin.x + MaxX * 8 + 12, Origin.y + 28);
+ if(TURBO || ptinrect(ghost.cpos, insetrect(Rpt(p, p), -4))){
InitMineField();
+ qlock(&ghost);
+ ghost.active = 0;
+ ghost.targettype = NOTARG;
+ ghost.generation++;
+ qunlock(&ghost);
eresized(0);
- }
- goto move;
+ }else
+ approach(p);
+ return;
}
- if(!ghostactive)
- ghostfind();
- if(ghostactive > 0){
- p = addpt(addpt(mulpt(ghosttarget, 16), Pt(12+8, 57+8)), Origin);
- if(ptinrect(LastMouse.xy, insetrect(Rpt(p, p), -4))){
- switch(ghostactive){
- case 1: LeftClick(ghosttarget); break;
- case 2: RightClick(ghosttarget); break;
+ qlock(&ghost);
+ if(!ghost.active){
+ ghost.active = 1;
+ rwakeup(&ghost);
+ }
+ if(ghost.targettype != NOTARG){
+ p = addpt(addpt(mulpt(ghost.target, 16), Pt(12+8, 57+8)), Origin);
+ if(TURBO || ptinrect(ghost.cpos, insetrect(Rpt(p, p), -4))){
+ switch(ghost.targettype){
+ case LTARG: LeftClick(ghost.target); break;
+ case RTARG: RightClick(ghost.target); break;
}
- if(Status != Game) ghostwait = 100;
+ ghost.targettype = NOTARG;
+ rwakeup(&ghost);
+ qunlock(&ghost);
+ if(Status != Game && !TURBO) ghost.wait = 100;
DrawButton(Status);
flushimage(display, 1);
- ghostactive = 0;
return;
+ }else{
+ qunlock(&ghost);
+ approach(p);
}
- move:
- q = subpt(p, LastMouse.xy);
- d = hypot(q.x, q.y);
- d = 2 / d * (1 + d / (400 + d));
- LastMouse.xy.x += ceil(q.x * d);
- LastMouse.xy.y += ceil(q.y * d);
- emoveto(LastMouse.xy);
- }
+ }else
+ qunlock(&ghost);
}
void
-GhostReset(void)
+GhostReset(int deltarg)
{
- ghostactive = 0;
- ghostwait = 0;
+ if(ghost.cursor){
+ esetcursor(nil);
+ ghost.cursor = 0;
+ }
+ ghost.cpos = LastMouse.xy;
+ qlock(&ghost);
+ ghost.active = 0;
+ ghost.wait = 0;
+ ghost.targettype = NOTARG;
+ if(deltarg)
+ ghost.generation++;
+ qunlock(&ghost);
}
--- a/sys/src/games/mines/mines.c
+++ b/sys/src/games/mines/mines.c
@@ -2,6 +2,7 @@
#include <libc.h>
#include <draw.h>
#include <event.h>
+#include <mp.h>
#include "dat.h"
#include "fns.h"
@@ -533,6 +534,7 @@
Event Event;
Point CurrentCell, Cell = Pt(-1, -1);
+ GhostInit();
Etimer = etimer(0, UseGhost ? 10 : 1000);
LastAction = nsec();
@@ -541,7 +543,7 @@
if(Key == Etimer) {
- if(nsec() - LastAction > 5000000000ULL && LastMouse.buttons == 0)
+ if(nsec() - LastAction > 1000000000ULL && LastMouse.buttons == 0)
GhostMode();
if(++Counter == (UseGhost ? 100 : 1)){
@@ -556,11 +558,11 @@
if(Key == Emouse) {
- if(!eqpt(LastMouse.xy, Event.mouse.xy) || LastMouse.buttons != Event.mouse.buttons){
+ if(!GhostCheck(Event.mouse.xy) || LastMouse.buttons != Event.mouse.buttons){
LastAction = nsec();
- GhostReset();
+ LastMouse = Event.mouse;
+ GhostReset(Event.mouse.buttons != 0);
}
- LastMouse = Event.mouse;
/* mouse over button? */
CurrentButton = FALSE;
@@ -666,7 +668,7 @@
if(Key == Ekeyboard) {
LastAction = nsec();
- GhostReset();
+ GhostReset(1);
switch(Event.kbdc) {
case 'n':