ghost in the minesweeper shell
This commit is contained in:
parent
f0a314605f
commit
4aff59b64c
5 changed files with 333 additions and 12 deletions
|
@ -46,13 +46,11 @@ struct {
|
||||||
int Mine, Picture, Neighbours;
|
int Mine, Picture, Neighbours;
|
||||||
} FieldCell;
|
} FieldCell;
|
||||||
|
|
||||||
struct {
|
|
||||||
int MaxX, MaxY, Mines;
|
|
||||||
} Settings[] = { {8, 8, 10}, {16, 16, 40}, {30, 16, 99}, {0, 0, 0} };
|
|
||||||
|
|
||||||
extern int MaxX, MaxY, Mines, Level, UnknownCell, Playing, MinesRemain, Time, Status, UseQuery, UseColor;
|
extern int MaxX, MaxY, Mines, Level, UnknownCell, Playing, MinesRemain, Time, Status, UseQuery, UseGhost, UseColor;
|
||||||
extern Point Origin;
|
extern Point Origin;
|
||||||
extern FieldCell **MineField;
|
extern FieldCell **MineField;
|
||||||
|
extern Mouse LastMouse;
|
||||||
|
|
||||||
extern uchar SrcDigit0[];
|
extern uchar SrcDigit0[];
|
||||||
extern uchar SrcDigit1[];
|
extern uchar SrcDigit1[];
|
||||||
|
|
6
sys/src/games/mines/fns.h
Normal file
6
sys/src/games/mines/fns.h
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
void LeftClick(Point);
|
||||||
|
void RightClick(Point);
|
||||||
|
void DrawButton(int);
|
||||||
|
void InitMineField(void);
|
||||||
|
void GhostMode(void);
|
||||||
|
void GhostReset(void);
|
283
sys/src/games/mines/ghost.c
Normal file
283
sys/src/games/mines/ghost.c
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <draw.h>
|
||||||
|
#include <event.h>
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fns.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MEmpty,
|
||||||
|
MMine,
|
||||||
|
MUnknown,
|
||||||
|
NState,
|
||||||
|
};
|
||||||
|
|
||||||
|
int ghostactive;
|
||||||
|
int ghostwait;
|
||||||
|
Point ghosttarget;
|
||||||
|
|
||||||
|
static uchar ***
|
||||||
|
neighbours(uchar **f, uchar ***n)
|
||||||
|
{
|
||||||
|
int x, y, p;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(y = 0; y < MaxY; y++)
|
||||||
|
for(x = 0; x < MaxX; x++){
|
||||||
|
p = f[x][y];
|
||||||
|
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 > 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]++;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
freeneighbours(uchar ***n)
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
if(n == nil)
|
||||||
|
return;
|
||||||
|
for(x = 0; x < MaxX; x++){
|
||||||
|
for(y = 0; y < MaxY; 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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
c = aux;
|
||||||
|
if(f[x][y] == MUnknown)
|
||||||
|
c->pt[c->pts++] = (Point){x,y};
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mklists(uchar **f, CList **clp, int *nclp)
|
||||||
|
{
|
||||||
|
CList *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++)
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
freeneighbours(nei);
|
||||||
|
*clp = cl;
|
||||||
|
*nclp = ncl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ismember(CList *c, Point p)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < c->pts; i++)
|
||||||
|
if(c->pt[i].x == p.x && c->pt[i].y == p.y)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
merge(CList *cl, int *nclp)
|
||||||
|
{
|
||||||
|
int i, j, k, l;
|
||||||
|
|
||||||
|
start:
|
||||||
|
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)--;
|
||||||
|
}
|
||||||
|
goto start;
|
||||||
|
next: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ghostfind(void)
|
||||||
|
{
|
||||||
|
int x, y, i, j, n;
|
||||||
|
uchar **field;
|
||||||
|
CList *cl;
|
||||||
|
int ncl;
|
||||||
|
Point pd;
|
||||||
|
int d, min;
|
||||||
|
|
||||||
|
field = calloc(sizeof(uchar*), MaxX);
|
||||||
|
for(x = 0; x < MaxX; x++){
|
||||||
|
field[x] = calloc(sizeof(uchar), MaxY);
|
||||||
|
for(y = 0; y < MaxY; y++)
|
||||||
|
switch(MineField[x][y].Picture){
|
||||||
|
case Empty0:
|
||||||
|
case Empty1:
|
||||||
|
case Empty2:
|
||||||
|
case Empty3:
|
||||||
|
case Empty4:
|
||||||
|
case Empty5:
|
||||||
|
case Empty6:
|
||||||
|
case Empty7:
|
||||||
|
case Empty8:
|
||||||
|
field[x][y] = MEmpty;
|
||||||
|
break;
|
||||||
|
case Mark:
|
||||||
|
field[x][y] = MMine;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
field[x][y] = MUnknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mklists(field, &cl, &ncl);
|
||||||
|
merge(cl, &ncl);
|
||||||
|
ghostactive = -1;
|
||||||
|
min = 0;
|
||||||
|
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);
|
||||||
|
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];
|
||||||
|
min = d;
|
||||||
|
}
|
||||||
|
field[cl[i].pt[j].x][cl[i].pt[j].y] = 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:;
|
||||||
|
}
|
||||||
|
for(x = 0; x < MaxX; x++)
|
||||||
|
free(field[x]);
|
||||||
|
free(field);
|
||||||
|
free(cl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GhostMode(void)
|
||||||
|
{
|
||||||
|
Point p, q;
|
||||||
|
double d;
|
||||||
|
|
||||||
|
if(ghostwait > 0){
|
||||||
|
ghostwait--;
|
||||||
|
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))){
|
||||||
|
InitMineField();
|
||||||
|
eresized(0);
|
||||||
|
}
|
||||||
|
goto move;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if(Status != Game) ghostwait = 100;
|
||||||
|
DrawButton(Status);
|
||||||
|
flushimage(display, 1);
|
||||||
|
ghostactive = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GhostReset(void)
|
||||||
|
{
|
||||||
|
ghostactive = 0;
|
||||||
|
ghostwait = 0;
|
||||||
|
}
|
|
@ -3,10 +3,16 @@
|
||||||
#include <draw.h>
|
#include <draw.h>
|
||||||
#include <event.h>
|
#include <event.h>
|
||||||
#include "dat.h"
|
#include "dat.h"
|
||||||
|
#include "fns.h"
|
||||||
|
|
||||||
int MaxX, MaxY, Mines, Level, UnknownCell, Playing, MinesRemain, Time, Status, UseQuery = TRUE, UseColor = TRUE;
|
struct {
|
||||||
|
int MaxX, MaxY, Mines;
|
||||||
|
} Settings[] = { {8, 8, 10}, {16, 16, 40}, {30, 16, 99}, {0, 0, 0} };
|
||||||
|
|
||||||
|
int MaxX, MaxY, Mines, Level, UnknownCell, Playing, MinesRemain, Time, Status, UseQuery = TRUE, UseGhost = FALSE, UseColor = TRUE;
|
||||||
|
|
||||||
Point Origin;
|
Point Origin;
|
||||||
|
Mouse LastMouse;
|
||||||
|
|
||||||
Image *RGB000000, *RGB0000FF, *RGB007F00, *RGB7F7F7F, *RGBBFBFBF, *RGBFF0000, *RGBFFFF00, *RGBFFFFFF, *ImageButton[5], *ImageSign, *ImageDigit[10], *ImageCell[16];
|
Image *RGB000000, *RGB0000FF, *RGB007F00, *RGB7F7F7F, *RGBBFBFBF, *RGBFF0000, *RGBFFFF00, *RGBFFFFFF, *ImageButton[5], *ImageSign, *ImageDigit[10], *ImageCell[16];
|
||||||
|
|
||||||
|
@ -374,13 +380,19 @@ void RightClick(Point Cell) {
|
||||||
|
|
||||||
void Usage(void) {
|
void Usage(void) {
|
||||||
|
|
||||||
fprint(2, "Usage: %s\n", argv0);
|
fprint(2, "Usage: %s [-aeq]\n", argv0);
|
||||||
exits("usage");
|
exits("usage");
|
||||||
}
|
}
|
||||||
|
|
||||||
void main(int argc, char **argv) {
|
void main(int argc, char **argv) {
|
||||||
|
|
||||||
|
Level = Beginner;
|
||||||
|
|
||||||
ARGBEGIN {
|
ARGBEGIN {
|
||||||
|
case 'a': Level = Advanced; break;
|
||||||
|
case 'e': Level = Expert; break;
|
||||||
|
case 'q': UseQuery = FALSE; break;
|
||||||
|
case 'g': UseGhost = TRUE; break;
|
||||||
default:
|
default:
|
||||||
Usage();
|
Usage();
|
||||||
} ARGEND
|
} ARGEND
|
||||||
|
@ -499,7 +511,7 @@ void main(int argc, char **argv) {
|
||||||
|
|
||||||
srand(time(0)); /* initialize generator of random numbers */
|
srand(time(0)); /* initialize generator of random numbers */
|
||||||
|
|
||||||
NewMineField(Beginner);
|
NewMineField(Level);
|
||||||
|
|
||||||
eresized(0);
|
eresized(0);
|
||||||
|
|
||||||
|
@ -507,23 +519,41 @@ void main(int argc, char **argv) {
|
||||||
|
|
||||||
{
|
{
|
||||||
int PushButton = FALSE, Button = FALSE, CurrentButton, ChargedButton = FALSE, MiddleButton = FALSE, LastButton = 0;
|
int PushButton = FALSE, Button = FALSE, CurrentButton, ChargedButton = FALSE, MiddleButton = FALSE, LastButton = 0;
|
||||||
|
int Counter = 0;
|
||||||
ulong Key, Etimer;
|
ulong Key, Etimer;
|
||||||
|
uvlong LastAction;
|
||||||
Event Event;
|
Event Event;
|
||||||
Point CurrentCell, Cell = Pt(-1, -1);
|
Point CurrentCell, Cell = Pt(-1, -1);
|
||||||
|
|
||||||
Etimer = etimer(0, 1000);
|
Etimer = etimer(0, UseGhost ? 10 : 1000);
|
||||||
|
LastAction = nsec();
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
Key = event(&Event);
|
Key = event(&Event);
|
||||||
|
|
||||||
if(Key == Etimer) {
|
if(Key == Etimer) {
|
||||||
|
|
||||||
if(Playing && Time < INT_MAX)
|
if(nsec() - LastAction > 5000000000ULL && LastMouse.buttons == 0)
|
||||||
DisplayCounter(Origin.x -34 + MaxX * 16, ++Time);
|
GhostMode();
|
||||||
|
|
||||||
|
if(++Counter == (UseGhost ? 100 : 1)){
|
||||||
|
|
||||||
|
Counter = 0;
|
||||||
|
|
||||||
|
if(Playing && Time < INT_MAX)
|
||||||
|
DisplayCounter(Origin.x -34 + MaxX * 16, ++Time);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Key == Emouse) {
|
if(Key == Emouse) {
|
||||||
|
|
||||||
|
if(!eqpt(LastMouse.xy, Event.mouse.xy) || LastMouse.buttons != Event.mouse.buttons){
|
||||||
|
LastAction = nsec();
|
||||||
|
GhostReset();
|
||||||
|
}
|
||||||
|
LastMouse = Event.mouse;
|
||||||
|
|
||||||
/* mouse over button? */
|
/* mouse over button? */
|
||||||
CurrentButton = FALSE;
|
CurrentButton = FALSE;
|
||||||
if(Event.mouse.xy.x > Origin.x + MaxX * 8 && Event.mouse.xy.x < Origin.x + MaxX * 8 + 25 && Event.mouse.xy.y > Origin.y + 17 && Event.mouse.xy.y < Origin.y + 42) CurrentButton = TRUE;
|
if(Event.mouse.xy.x > Origin.x + MaxX * 8 && Event.mouse.xy.x < Origin.x + MaxX * 8 + 25 && Event.mouse.xy.y > Origin.y + 17 && Event.mouse.xy.y < Origin.y + 42) CurrentButton = TRUE;
|
||||||
|
@ -627,6 +657,9 @@ void main(int argc, char **argv) {
|
||||||
|
|
||||||
if(Key == Ekeyboard) {
|
if(Key == Ekeyboard) {
|
||||||
|
|
||||||
|
LastAction = nsec();
|
||||||
|
GhostReset();
|
||||||
|
|
||||||
switch(Event.kbdc) {
|
switch(Event.kbdc) {
|
||||||
case 'n':
|
case 'n':
|
||||||
case 'N':
|
case 'N':
|
||||||
|
|
|
@ -4,9 +4,10 @@ TARG=mines
|
||||||
|
|
||||||
OFILES=\
|
OFILES=\
|
||||||
mines.$O \
|
mines.$O \
|
||||||
|
ghost.$O \
|
||||||
gfx.$O \
|
gfx.$O \
|
||||||
|
|
||||||
HFILES=dat.h
|
HFILES=dat.h fns.h
|
||||||
|
|
||||||
BIN=/$objtype/bin/games
|
BIN=/$objtype/bin/games
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue