From 7ca11774a1a78dcfa520f35a60d6ce34c2dc724a Mon Sep 17 00:00:00 2001 From: aiju Date: Sat, 10 Mar 2018 14:40:34 +0000 Subject: [PATCH] games/mines: build a better ghost trap --- sys/src/games/mines/fns.h | 4 +- sys/src/games/mines/ghost.c | 859 +++++++++++++++++++++++++++++------- sys/src/games/mines/mines.c | 12 +- 3 files changed, 712 insertions(+), 163 deletions(-) diff --git a/sys/src/games/mines/fns.h b/sys/src/games/mines/fns.h index 6bd84b744..3c7816c95 100644 --- a/sys/src/games/mines/fns.h +++ b/sys/src/games/mines/fns.h @@ -2,5 +2,7 @@ void LeftClick(Point); void RightClick(Point); void DrawButton(int); void InitMineField(void); +void GhostInit(void); void GhostMode(void); -void GhostReset(void); +void GhostReset(int); +int GhostCheck(Point); diff --git a/sys/src/games/mines/ghost.c b/sys/src/games/mines/ghost.c index 80e8be24a..7f6375733 100644 --- a/sys/src/games/mines/ghost.c +++ b/sys/src/games/mines/ghost.c @@ -2,9 +2,18 @@ #include #include #include +#include +#include #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 @@ enum { 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,68 +94,62 @@ static void 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]); } 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 @@ mklists(uchar **f, CList **clp, int *nclp) } 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; + } + 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]; } - goto start; 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 @@ ghostfind(void) 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 +GhostInit(void) +{ + 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; + + 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, q; - double d; + Point p; - if(ghostwait > 0){ - ghostwait--; + 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); } diff --git a/sys/src/games/mines/mines.c b/sys/src/games/mines/mines.c index d9d15ce18..80d3f3e94 100644 --- a/sys/src/games/mines/mines.c +++ b/sys/src/games/mines/mines.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "dat.h" #include "fns.h" @@ -533,6 +534,7 @@ void main(int argc, char **argv) { Event Event; Point CurrentCell, Cell = Pt(-1, -1); + GhostInit(); Etimer = etimer(0, UseGhost ? 10 : 1000); LastAction = nsec(); @@ -541,7 +543,7 @@ void main(int argc, char **argv) { 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 @@ void main(int argc, char **argv) { 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 @@ void main(int argc, char **argv) { if(Key == Ekeyboard) { LastAction = nsec(); - GhostReset(); + GhostReset(1); switch(Event.kbdc) { case 'n':