]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/mines/mines.c
games/mines: bikeshedding
[plan9front.git] / sys / src / games / mines / mines.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <event.h>
5 #include "dat.h"
6
7 int MaxX, MaxY, Mines, Level, UnknownCell, Playing, MinesRemain, Time, Status, UseQuery = TRUE, UseColor = TRUE;
8
9 Point Origin;
10
11 Image *RGB000000, *RGB0000FF, *RGB007F00, *RGB7F7F7F, *RGBBFBFBF, *RGBFF0000, *RGBFFFF00, *RGBFFFFFF, *ImageButton[5], *ImageSign, *ImageDigit[10], *ImageCell[16];
12
13 FieldCell **MineField;
14
15 void Pack(Point *p, Point p0, Point p1, Point p2, Point p3, Point p4, Point p5) {
16
17         p[0] = p0;
18         p[1] = p1;
19         p[2] = p2;
20         p[3] = p3;
21         p[4] = p4;
22         p[5] = p5;
23 }
24
25 void Button3d(Image *im, Rectangle r, int i, Image *color1, Image *color2, Image *color3, Point sp) {
26
27         Point p[6];
28
29         if(i < 0){
30                 r = insetrect(r, i);
31                 i = -i;
32         }
33
34         draw(im, Rect(r.min.x + i, r.min.y + i, r.max.x - i, r.max.y - i), color1, nil, Pt(sp.x + i, sp.y + i));
35
36         Pack(p, r.min, Pt(r.min.x, r.max.y), Pt(r.min.x + i, r.max.y - i), Pt(r.min.x + i, r.min.y + i), Pt(r.max.x - i, r.min.y + i), Pt(r.max.x, r.min.y));
37         fillpoly(im, p, 6, 0, color2, sp);
38
39         Pack(p, r.max, Pt(r.min.x, r.max.y), Pt(r.min.x + i, r.max.y - i), Pt(r.max.x - i, r.max.y - i), Pt(r.max.x - i, r.min.y + i), Pt(r.max.x, r.min.y));
40         fillpoly(im, p, 6, 0, color3, sp);
41 }
42
43 void DisplayCounter(int x, int n) {
44
45         Image *A, *B, *C;
46
47         Button3d(screen, Rect(x, Origin.y + 16, x + 41, Origin.y + 41), 1, RGB000000, RGB7F7F7F, RGBFFFFFF, ZP);
48
49         if(n < -99) n = -99;
50         if(n > 999) n = 999;
51
52         A = ImageDigit[abs(n / 100 - n / 1000 * 10)];
53         B = ImageDigit[abs(n / 10 - n / 100 * 10)];
54         C = ImageDigit[abs(n / 1 - n / 10 * 10)];
55
56         if(n < 0) A = ImageSign;
57
58         draw(screen, Rect(x + 2, Origin.y + 18, x + 13, Origin.y + 39), A, nil, ZP);
59         draw(screen, Rect(x + 15, Origin.y + 18, x + 26, Origin.y + 39), B, nil, ZP);
60         draw(screen, Rect(x + 28, Origin.y + 18, x + 39, Origin.y + 39), C, nil, ZP);
61 }
62
63 void DrawButton(int Index) {
64
65         draw(screen, Rect(Origin.x +  MaxX * 8 - 1, Origin.y + 16, Origin.x + MaxX * 8 + 24, Origin.y + 41), ImageButton[Index], nil, ZP);
66 }
67
68 void DrawCell(Point Cell) {
69
70         draw(screen, Rect(Origin.x + 12 + Cell.x * 16, Origin.y + 57 + Cell.y * 16, Origin.x + 12 + Cell.x * 16 + Dx(ImageCell[MineField[Cell.x][Cell.y].Picture]->r), Origin.y + 57 + Cell.y * 16 + Dy(ImageCell[MineField[Cell.x][Cell.y].Picture]->r)), ImageCell[MineField[Cell.x][Cell.y].Picture], nil, ZP);
71 }
72
73 void eresized(int New) {
74
75         if(New && getwindow(display, Refmesg) < 0)
76                 fprint(2,"%s: can't reattach to window", argv0);
77
78         Origin.x = screen->r.min.x + (screen->r.max.x - screen->r.min.x - 23 - MaxX * 16) / 2;
79         Origin.y = screen->r.min.y + (screen->r.max.y - screen->r.min.y - 68 - MaxY * 16) / 2;
80
81         draw(screen, screen->r, RGB0000FF, nil, ZP);
82
83         /* main window */
84         Button3d(screen, Rect(Origin.x, Origin.y, Origin.x + 23 + MaxX * 16, Origin.y + 68 + MaxY * 16), 3, RGBBFBFBF, RGBFFFFFF, RGB7F7F7F, ZP);
85
86         /* top small window with button and 2 counters */
87         Button3d(screen, Rect(Origin.x + 9, Origin.y + 9, Origin.x + 14 + MaxX * 16, Origin.y + 48), 2, RGBBFBFBF, RGB7F7F7F, RGBFFFFFF, ZP);
88
89         /* button */
90         DrawButton(Status);
91
92         /* counter on the left side - remaining mines */
93         DisplayCounter(Origin.x + 16, MinesRemain);
94
95         /* counter on the right side - timer */
96         DisplayCounter(Origin.x -34 + MaxX * 16, Time);
97
98         /* bottom window - mine field */
99         Button3d(screen, Rect(Origin.x + 9, Origin.y + 54, Origin.x + 14 + MaxX * 16, Origin.y + 59 + MaxY * 16), 3, RGBBFBFBF, RGB7F7F7F, RGBFFFFFF, ZP);
100
101         {
102                 int x, y;
103
104                 for(x = 1; x < MaxX; x++)
105                         line(screen, Pt(Origin.x + 11 + x * 16, Origin.y + 57), Pt(Origin.x + 11 + x * 16, Origin.y + 55 + MaxY * 16), Endsquare, Endsquare, 0, RGB7F7F7F, ZP);
106
107                 for(y = 1; y < MaxY; y++)
108                         line(screen, Pt(Origin.x + 12, Origin.y + 56 + y * 16), Pt(Origin.x + 10 + MaxX * 16, Origin.y + 56 + y * 16), Endsquare, Endsquare, 0, RGB7F7F7F, ZP);
109
110                 for(y = 0; y < MaxY; y++)
111                         for(x = 0; x < MaxX; x++)
112                                 DrawCell(Pt(x, y));
113         }
114 }
115
116 void MouseCell(Point Cell) {
117
118         int Picture;
119
120         switch(MineField[Cell.x][Cell.y].Picture) {
121                 case Unknown:
122                         Picture = Empty0;
123                         break;
124                 case Query:
125                         Picture = MouseQuery;
126                         break;
127                 default:
128                         return;
129         }
130         draw(screen, Rect(Origin.x + 12 + Cell.x * 16, Origin.y + 57 + Cell.y * 16, Origin.x + 12 + Cell.x * 16 + Dx(ImageCell[Picture]->r), Origin.y + 57 + Cell.y * 16 + Dy(ImageCell[Picture]->r)), ImageCell[Picture], nil, ZP);
131 }
132
133 void RecoverCell(Point Cell) {
134
135         switch(MineField[Cell.x][Cell.y].Picture) {
136                 case Unknown:
137                 case Query:
138                         draw(screen, Rect(Origin.x + 12 + Cell.x * 16, Origin.y + 57 + Cell.y * 16, Origin.x + 12 + Cell.x * 16 + Dx(ImageCell[MineField[Cell.x][Cell.y].Picture]->r), Origin.y + 57 + Cell.y * 16 + Dy(ImageCell[MineField[Cell.x][Cell.y].Picture]->r)), ImageCell[MineField[Cell.x][Cell.y].Picture], nil, ZP);
139         }
140 }
141
142 void *emalloc(ulong size) {
143
144         void *p;
145
146         p = malloc(size);
147         if(p == 0) {
148                 fprint(2, "%s: no memory: %r\n", argv0);
149                 exits("no mem");
150         }
151         return p;
152 }
153
154 void InitMineField(void) {
155
156         /* clean up mine field, make all cells unknown and place new mines */
157         {
158                 int i, x, y;
159
160                 for(y = 0; y < MaxY; y++)
161                         for(x = 0; x < MaxX; x++) {
162                                 MineField[x][y].Mine = FALSE;
163                                 MineField[x][y].Picture = Unknown;
164                         }
165
166                 for(i = 0; i < Mines; ) {
167                         x = nrand(MaxX);
168                         y = nrand(MaxY);
169                         if(MineField[x][y].Mine) continue;
170                         MineField[x][y].Mine = TRUE;
171                         i++;
172                 }
173         }
174
175         /* count mines in neighbourhood */
176         {
177                 int x, y;
178
179                 for(y = 0; y < MaxY; y++)
180                         for(x = 0; x < MaxX; x++) {
181                                 MineField[x][y].Neighbours = 0;
182                                 if(x > 0 && MineField[x - 1][y].Mine) MineField[x][y].Neighbours++;
183                                 if(y > 0 && MineField[x][y - 1].Mine) MineField[x][y].Neighbours++;
184                                 if(x < MaxX -1 && MineField[x + 1][y].Mine) MineField[x][y].Neighbours++;
185                                 if(y < MaxY - 1 && MineField[x][y + 1].Mine) MineField[x][y].Neighbours++;
186                                 if(x > 0 && y > 0 && MineField[x - 1][y - 1].Mine) MineField[x][y].Neighbours++;
187                                 if(x > 0 && y < MaxY - 1 && MineField[x - 1][y + 1].Mine) MineField[x][y].Neighbours++;
188                                 if(x < MaxX - 1 && y > 0 && MineField[x + 1][y - 1].Mine) MineField[x][y].Neighbours++;
189                                 if(x < MaxX - 1 && y < MaxY - 1 && MineField[x + 1][y + 1].Mine) MineField[x][y].Neighbours++;
190                         }
191         }
192
193         Status = Game;
194         Playing = FALSE;
195         MinesRemain = Mines;
196         Time = 0;
197         UnknownCell = MaxX * MaxY - Mines;
198 }
199
200 void NewMineField(int NewLevel) {
201
202         int CurrentMaxX, CurrentMaxY;
203
204         CurrentMaxX = MaxX;
205         CurrentMaxY = MaxY;
206
207         Level = NewLevel;
208
209         /* set size of mine field and number of mines */
210         if(Level == Custom) {
211
212                 /* here should ask a player about custom size of mine field and number of mines; in next release, may be... */
213
214                 if(MaxX < 8) MaxX = 8;
215                 if(MaxY < 8) MaxY = 8;
216                 if(Mines < 10) Mines = 10;
217                 if(MaxX > 30) MaxX = 30;
218                 if(MaxY > 24) MaxY = 24;
219                 if(Mines > (MaxX - 1) * (MaxY - 1)) Mines = (MaxX - 1) * (MaxY - 1);
220         }
221         else {
222                 MaxX = Settings[Level].MaxX;
223                 MaxY = Settings[Level].MaxY;
224                 Mines = Settings[Level].Mines;
225                 Settings[Custom].MaxX = MaxX;
226                 Settings[Custom].MaxY = MaxY;
227                 Settings[Custom].Mines = Mines;
228         }
229
230         if(MaxX != CurrentMaxX || MaxY != CurrentMaxY) {
231
232                 int x;
233
234                 /* if some memory is in use, release it */
235                 if(MineField) {
236                         for(x = 0; x < CurrentMaxX; x++)
237                                 free(MineField[x]);
238                         free(MineField);
239                 }
240
241                 /* allocate new memory */
242                 MineField = (FieldCell **)emalloc(MaxX * sizeof(FieldCell *));
243                 for(x = 0; x < MaxX; x++)
244                         MineField[x] = (FieldCell *)emalloc(MaxY * sizeof(FieldCell));
245         }
246         InitMineField();
247 }
248
249 void GameOver(void) {
250
251         int x, y;
252
253         Playing = FALSE;
254         for(y = 0; y < MaxY; y++)
255                 for(x = 0; x < MaxX; x++)
256                         switch(MineField[x][y].Picture) {
257                                 case Unknown:
258                                 case Query:
259                                         if(MineField[x][y].Mine) {
260                                                 switch(Status) {
261                                                         case Win:
262                                                                 MineField[x][y].Picture = Mark;
263                                                                 DisplayCounter(Origin.x + 16, --MinesRemain);
264                                                                 break;
265                                                         case Oops:
266                                                                 MineField[x][y].Picture = Mined;
267                                                 }
268                                                 DrawCell(Pt(x, y));
269                                         }
270                                         break;
271                                 case Mark:
272                                         if(! MineField[x][y].Mine) {
273                                                 MineField[x][y].Picture = Fault;
274                                                 DrawCell(Pt(x, y));
275                                         }
276                         }
277 }
278
279 void LeftClick(Point Cell) {
280
281         if(! (Status == Game) || Cell.x < 0 || Cell.y < 0) return;
282         Playing = TRUE;
283         switch(MineField[Cell.x][Cell.y].Picture) {
284                 case Query:
285                 case Unknown:
286                         if(MineField[Cell.x][Cell.y].Mine) {
287                                 MineField[Cell.x][Cell.y].Picture = Explosion;
288                                 DrawCell(Cell);
289                                 Status = Oops;
290                                 GameOver();
291                                 return;
292                         }
293                         MineField[Cell.x][Cell.y].Picture = MineField[Cell.x][Cell.y].Neighbours;
294                         DrawCell(Cell);
295                         if(! --UnknownCell) {
296                                 Status = Win;
297                                 GameOver();
298                         }
299                         else if(MineField[Cell.x][Cell.y].Neighbours == 0) {
300                                 if(Cell.x > 0) LeftClick(Pt(Cell.x - 1, Cell.y));
301                                 if(Cell.y > 0) LeftClick(Pt(Cell.x, Cell.y - 1));
302                                 if(Cell.x < MaxX - 1) LeftClick(Pt(Cell.x + 1, Cell.y));
303                                 if(Cell.y < MaxY - 1) LeftClick(Pt(Cell.x, Cell.y + 1));
304                                 if(Cell.x > 0 && Cell.y > 0) LeftClick(Pt(Cell.x - 1, Cell.y - 1));
305                                 if(Cell.x > 0 && Cell.y < MaxY - 1) LeftClick(Pt(Cell.x - 1, Cell.y + 1));
306                                 if(Cell.x < MaxX - 1 && Cell.y > 0) LeftClick(Pt(Cell.x + 1, Cell.y - 1));
307                                 if(Cell.x < MaxX - 1 && Cell.y < MaxY - 1) LeftClick(Pt(Cell.x + 1, Cell.y + 1));
308                         }
309         }
310 }
311
312 void MiddleClick(Point Cell) {
313
314         int Neighbours = 0;
315
316         if(! (Status == Game) || Cell.x < 0 || Cell.y < 0) return;
317         Playing = TRUE;
318         switch(MineField[Cell.x][Cell.y].Picture) {
319                 case Empty1:
320                 case Empty2:
321                 case Empty3:
322                 case Empty4:
323                 case Empty5:
324                 case Empty6:
325                 case Empty7:
326                 case Empty8:
327                         break;
328                 default:
329                          return;
330         }
331
332         if(Cell.x > 0 && MineField[Cell.x - 1][Cell.y].Picture == Mark) Neighbours++;
333         if(Cell.y > 0 && MineField[Cell.x][Cell.y - 1].Picture == Mark) Neighbours++;
334         if(Cell.x < MaxX - 1 && MineField[Cell.x + 1][Cell.y].Picture == Mark) Neighbours++;
335         if(Cell.y < MaxY - 1 && MineField[Cell.x][Cell.y + 1].Picture == Mark) Neighbours++;
336         if(Cell.x > 0 && Cell.y > 0 && MineField[Cell.x - 1][Cell.y - 1].Picture == Mark) Neighbours++;
337         if(Cell.x > 0 && Cell.y < MaxY - 1 && MineField[Cell.x - 1][Cell.y + 1].Picture == Mark) Neighbours++;
338         if(Cell.x < MaxX - 1 && Cell.y > 0 && MineField[Cell.x + 1][Cell.y - 1].Picture == Mark) Neighbours++;
339         if(Cell.x < MaxX - 1 && Cell.y < MaxY - 1 && MineField[Cell.x + 1][Cell.y + 1].Picture == Mark) Neighbours++;
340
341         if(Neighbours == MineField[Cell.x][Cell.y].Neighbours) {
342                 if(Cell.x > 0) LeftClick(Pt(Cell.x - 1, Cell.y));
343                 if(Cell.y > 0) LeftClick(Pt(Cell.x, Cell.y - 1));
344                 if(Cell.x < MaxX - 1) LeftClick(Pt(Cell.x + 1, Cell.y));
345                 if(Cell.y < MaxY - 1) LeftClick(Pt(Cell.x, Cell.y + 1));
346                 if(Cell.x > 0 && Cell.y > 0) LeftClick(Pt(Cell.x - 1, Cell.y - 1));
347                 if(Cell.x > 0 && Cell.y < MaxY - 1) LeftClick(Pt(Cell.x - 1, Cell.y + 1));
348                 if(Cell.x < MaxX - 1 && Cell.y > 0) LeftClick(Pt(Cell.x + 1, Cell.y - 1));
349                 if(Cell.x < MaxX - 1 && Cell.y < MaxY - 1) LeftClick(Pt(Cell.x + 1, Cell.y + 1));
350         }
351 }
352
353 void RightClick(Point Cell) {
354
355         if(! (Status == Game) || Cell.x < 0 || Cell.y < 0) return;
356
357         Playing = TRUE;
358         switch(MineField[Cell.x][Cell.y].Picture) {
359                 case Unknown:
360                         MineField[Cell.x][Cell.y].Picture = Mark;
361                         DrawCell(Cell);
362                         DisplayCounter(Origin.x + 16, --MinesRemain);
363                         break;
364                 case Mark:
365                         MineField[Cell.x][Cell.y].Picture = (UseQuery ? Query : Unknown);
366                         DrawCell(Cell);
367                         DisplayCounter(Origin.x + 16, ++MinesRemain);
368                         break;
369                 case Query:
370                         MineField[Cell.x][Cell.y].Picture = Unknown;
371                         DrawCell(Cell);
372         }
373 }
374
375 void Usage(void) {
376
377         fprint(2, "Usage: %s\n", argv0);
378         exits("usage");
379 }
380
381 void main(int argc, char **argv) {
382
383         ARGBEGIN {
384         default:
385                 Usage();
386         } ARGEND
387
388         if(argc > 0) Usage();
389
390         if(initdraw(nil, nil, "mines") < 0) {
391                 fprint(2, "%s: can't open display: %r\n", argv0);
392                 exits("initdraw");
393         }
394
395         RGB000000 = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x000000ff);
396         RGB0000FF = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x0000ffff);
397         RGB007F00 = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x007f00ff);
398         RGB7F7F7F = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x7f7f7fff);
399         RGBBFBFBF = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xbfbfbfff);
400         RGBFF0000 = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xff0000ff);
401         RGBFFFF00 = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xffff00ff);
402         RGBFFFFFF = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xffffffff);
403
404         ImageButton[Game] = allocimage(display, Rect(0, 0, 25, 25), RGB24, 0, DNofill);
405         loadimage(ImageButton[Game], ImageButton[Game]->r, SrcButtonGame, ButtonBytes);
406
407         ImageButton[Push] = allocimage(display, Rect(0, 0, 25, 25), RGB24, 0, DNofill);
408         loadimage(ImageButton[Push], ImageButton[Push]->r, SrcButtonPush, ButtonBytes);
409
410         ImageButton[Move] = allocimage(display, Rect(0, 0, 25, 25), RGB24, 0, DNofill);
411         loadimage(ImageButton[Move], ImageButton[Move]->r, SrcButtonMove, ButtonBytes);
412
413         ImageButton[Win] = allocimage(display, Rect(0, 0, 25, 25), RGB24, 0, DNofill);
414         loadimage(ImageButton[Win], ImageButton[Win]->r, SrcButtonWin, ButtonBytes);
415
416         ImageButton[Oops] = allocimage(display, Rect(0, 0, 25, 25), RGB24, 0, DNofill);
417         loadimage(ImageButton[Oops], ImageButton[Oops]->r, SrcButtonOops, ButtonBytes);
418
419         ImageSign = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
420         loadimage(ImageSign, ImageSign->r, SrcSign, DigitBytes);
421
422         ImageDigit[0] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
423         loadimage(ImageDigit[0], ImageDigit[0]->r, SrcDigit0, DigitBytes);
424
425         ImageDigit[1] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
426         loadimage(ImageDigit[1], ImageDigit[1]->r, SrcDigit1, DigitBytes);
427
428         ImageDigit[2] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
429         loadimage(ImageDigit[2], ImageDigit[2]->r, SrcDigit2, DigitBytes);
430
431         ImageDigit[3] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
432         loadimage(ImageDigit[3], ImageDigit[3]->r, SrcDigit3, DigitBytes);
433
434         ImageDigit[4] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
435         loadimage(ImageDigit[4], ImageDigit[4]->r, SrcDigit4, DigitBytes);
436
437         ImageDigit[5] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
438         loadimage(ImageDigit[5], ImageDigit[5]->r, SrcDigit5, DigitBytes);
439
440         ImageDigit[6] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
441         loadimage(ImageDigit[6], ImageDigit[6]->r, SrcDigit6, DigitBytes);
442
443         ImageDigit[7] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
444         loadimage(ImageDigit[7], ImageDigit[7]->r, SrcDigit7, DigitBytes);
445
446         ImageDigit[8] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
447         loadimage(ImageDigit[8], ImageDigit[8]->r, SrcDigit8, DigitBytes);
448
449         ImageDigit[9] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
450         loadimage(ImageDigit[9], ImageDigit[9]->r, SrcDigit9, DigitBytes);
451
452         ImageCell[Empty0] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
453         loadimage(ImageCell[Empty0], ImageCell[Empty0]->r, SrcEmpty0, CellBytes);
454
455         ImageCell[Empty1] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
456         loadimage(ImageCell[Empty1], ImageCell[Empty1]->r, SrcEmpty1, CellBytes);
457
458         ImageCell[Empty2] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
459         loadimage(ImageCell[Empty2], ImageCell[Empty2]->r, SrcEmpty2, CellBytes);
460
461         ImageCell[Empty3] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
462         loadimage(ImageCell[Empty3], ImageCell[Empty3]->r, SrcEmpty3, CellBytes);
463
464         ImageCell[Empty4] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
465         loadimage(ImageCell[Empty4], ImageCell[Empty4]->r, SrcEmpty4, CellBytes);
466
467         ImageCell[Empty5] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
468         loadimage(ImageCell[Empty5], ImageCell[Empty5]->r, SrcEmpty5, CellBytes);
469
470         ImageCell[Empty6] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
471         loadimage(ImageCell[Empty6], ImageCell[Empty6]->r, SrcEmpty6, CellBytes);
472
473         ImageCell[Empty7] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
474         loadimage(ImageCell[Empty7], ImageCell[Empty7]->r, SrcEmpty7, CellBytes);
475
476         ImageCell[Empty8] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
477         loadimage(ImageCell[Empty8], ImageCell[Empty8]->r, SrcEmpty8, CellBytes);
478
479         ImageCell[Unknown] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
480         loadimage(ImageCell[Unknown], ImageCell[Unknown]->r, SrcUnknown, CellBytes);
481
482         ImageCell[Mark] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
483         loadimage(ImageCell[Mark], ImageCell[Mark]->r, SrcMark, CellBytes);
484
485         ImageCell[Query] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
486         loadimage(ImageCell[Query], ImageCell[Query]->r, SrcQuery, CellBytes);
487
488         ImageCell[MouseQuery] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
489         loadimage(ImageCell[MouseQuery], ImageCell[MouseQuery]->r, SrcMouseQuery, CellBytes);
490
491         ImageCell[Mined] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
492         loadimage(ImageCell[Mined], ImageCell[Mined]->r, SrcMined, CellBytes);
493
494         ImageCell[Explosion] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
495         loadimage(ImageCell[Explosion], ImageCell[Explosion]->r, SrcExplosion, CellBytes);
496
497         ImageCell[Fault] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
498         loadimage(ImageCell[Fault], ImageCell[Fault]->r, SrcFault, CellBytes);
499
500         srand(time(0)); /* initialize generator of random numbers */
501
502         NewMineField(Beginner);
503
504         eresized(0);
505
506         einit(Emouse | Ekeyboard);
507
508         {
509                 int PushButton = FALSE, Button = FALSE, CurrentButton, ChargedButton = FALSE, MiddleButton = FALSE, LastButton = 0;
510                 ulong Key, Etimer;
511                 Event Event;
512                 Point CurrentCell, Cell = Pt(-1, -1);
513
514                 Etimer = etimer(0, 1000);
515
516                 for(;;) {
517                         Key = event(&Event);
518
519                         if(Key == Etimer) {
520
521                                 if(Playing && Time < INT_MAX)
522                                         DisplayCounter(Origin.x -34 + MaxX * 16, ++Time);
523                         }
524
525                         if(Key == Emouse) {
526
527                                 /* mouse over button? */
528                                 CurrentButton = FALSE;
529                                 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;
530
531                                 /* mouse over any cell? */
532                                 CurrentCell = Pt(-1, -1);
533                                 if(Event.mouse.xy.x > Origin.x + 12 && Event.mouse.xy.x < Origin.x + 12 + MaxX * 16)
534                                         CurrentCell.x = (Event.mouse.xy.x - Origin.x - 13) / 16;
535                                 if(Event.mouse.xy.y > Origin.y + 57 && Event.mouse.xy.y < Origin.y + 57 + MaxY * 16)
536                                         CurrentCell.y = (Event.mouse.xy.y - Origin.y - 58) / 16;
537
538                                 /* pressed mouse button */
539                                 if(Event.mouse.buttons > LastButton) {
540
541                                         if(CurrentButton)
542                                                 switch(Event.mouse.buttons) {
543                                                         case 1:
544                                                                 PushButton = TRUE;
545                                                                 break;
546                                                         default:
547                                                                 if(PushButton) DrawButton(Status);
548                                                                 PushButton = FALSE;
549                                                 }
550
551                                         if(! (CurrentCell.x < 0) && ! (CurrentCell.y < 0)) {
552                                                 switch(Event.mouse.buttons) {
553                                                         case 1:
554                                                                 ChargedButton = TRUE;
555                                                                 MiddleButton = FALSE;
556                                                                 break;
557                                                         case 2:
558                                                                 if(LastButton == 0) ChargedButton = TRUE;
559                                                                 MiddleButton = TRUE;
560                                                                 break;
561                                                         case 4:
562                                                                 if(LastButton == 0) RightClick(CurrentCell);
563                                                                 else MiddleButton = TRUE;
564                                                                 break;
565                                                         default:
566                                                                 ChargedButton = TRUE;
567                                                                 MiddleButton = TRUE;
568                                                 }
569                                                 if(ChargedButton && Status == Game) DrawButton(Move);
570                                         }
571                                 }
572
573                                 if(PushButton && CurrentButton != Button) {
574                                         if(CurrentButton) DrawButton(Push);
575                                         else DrawButton(Status);
576                                         Button = CurrentButton;
577                                 }
578
579                                 if(ChargedButton && (! eqpt(CurrentCell, Cell) || Event.mouse.buttons != LastButton) && Status == Game) {
580                                         if(! (Cell.x < 0) && ! (Cell.y < 0)) {
581                                                 RecoverCell(Cell);
582                                                 if(Cell.x > 0) RecoverCell(Pt(Cell.x - 1, Cell.y));
583                                                 if(Cell.y > 0) RecoverCell(Pt(Cell.x, Cell.y - 1));
584                                                 if(Cell.x < MaxX - 1) RecoverCell(Pt(Cell.x + 1, Cell.y));
585                                                 if(Cell.y < MaxY - 1) RecoverCell(Pt(Cell.x, Cell.y + 1));
586                                                 if(Cell.x > 0 && Cell.y > 0) RecoverCell(Pt(Cell.x - 1, Cell.y - 1));
587                                                 if(Cell.x > 0 && Cell.y < MaxY - 1) RecoverCell(Pt(Cell.x - 1, Cell.y + 1));
588                                                 if(Cell.x < MaxX - 1 && Cell.y > 0) RecoverCell(Pt(Cell.x + 1, Cell.y - 1));
589                                                 if(Cell.x < MaxX - 1 && Cell.y < MaxY - 1) RecoverCell(Pt(Cell.x + 1, Cell.y + 1));
590                                         }
591                                         Cell = CurrentCell;
592                                         if(! (Cell.x < 0) && ! (Cell.y < 0) && ! (Event.mouse.buttons < LastButton)) {
593                                                 MouseCell(Cell);
594                                                 if(MiddleButton) {
595                                                         if(Cell.x > 0) MouseCell(Pt(Cell.x - 1, Cell.y));
596                                                         if(Cell.y > 0) MouseCell(Pt(Cell.x, Cell.y - 1));
597                                                         if(Cell.x < MaxX - 1) MouseCell(Pt(Cell.x + 1, Cell.y));
598                                                         if(Cell.y < MaxY - 1) MouseCell(Pt(Cell.x, Cell.y + 1));
599                                                         if(Cell.x > 0 && Cell.y > 0) MouseCell(Pt(Cell.x - 1, Cell.y - 1));
600                                                         if(Cell.x > 0 && Cell.y < MaxY - 1) MouseCell(Pt(Cell.x - 1, Cell.y + 1));
601                                                         if(Cell.x < MaxX - 1 && Cell.y > 0) MouseCell(Pt(Cell.x + 1, Cell.y - 1));
602                                                         if(Cell.x < MaxX - 1 && Cell.y < MaxY - 1) MouseCell(Pt(Cell.x + 1, Cell.y + 1));
603                                                 }
604                                         }
605                                 }
606
607                                 /* released mouse button */
608                                 if(Event.mouse.buttons < LastButton) {
609                                         if(PushButton && CurrentButton) {
610                                                 InitMineField();
611                                                 eresized(0);
612                                                 }
613                                         Button = FALSE;
614                                         PushButton = FALSE;
615
616                                         if(ChargedButton) {
617                                                 if(MiddleButton) MiddleClick(Cell);
618                                                 else LeftClick(Cell);
619                                                 DrawButton(Status);
620                                         }
621                                         Cell = Pt(-1, -1);
622                                         ChargedButton = FALSE;
623                                 }
624
625                                 LastButton = Event.mouse.buttons;
626                         }
627
628                         if(Key == Ekeyboard) {
629
630                                 switch(Event.kbdc) {
631                                         case 'n':
632                                         case 'N':
633                                                 InitMineField();
634                                                 eresized(0);
635                                                 break;
636                                         case 'b':
637                                         case 'B':
638                                                 NewMineField(Beginner);
639                                                 eresized(0);
640                                                 break;
641                                         case 'a':
642                                         case 'A':
643                                                 NewMineField(Advanced);
644                                                 eresized(0);
645                                                 break;
646                                         case 'e':
647                                         case 'E':
648                                                 NewMineField(Expert);
649                                                 eresized(0);
650                                                 break;
651                                         case 'q':
652                                         case 'Q':
653                                         case 127:
654                                                 exits(nil);
655                                 }
656                         }
657                 }
658         }
659 }