1 /***************************************
2 XBomb - 'Minesweeper' game - Version 2.2b.
3
4 Main function of program - excludes all window interface parts.
5 ******************/ /******************
6 Written by Andrew M. Bishop
7
8 This file Copyright 1994-2014 Andrew M. Bishop
9 It may be distributed under the GNU Public License, version 2, or
10 any higher version. See section COPYING of the GNU Public license
11 for conditions under which this file may be redistributed.
12 ***************************************/
13
14
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <time.h>
19 #include <ctype.h>
20
21 #if defined(__sun__) && !defined(__svr4__)
22 int printf(const char*, ... );
23 int tolower (int c);
24 time_t time(time_t *t);
25 int rand(void);
26 #endif
27
28 #include "xbomb.h"
29
30 /*+ The names of the different game +*/
31 char *levels[NLEVELS]={"Easy","Medium","Difficult"}, /*+ levels (difficulty). +*/
32 *types [NTYPES] ={"Hexagons","Squares","Triangles"}; /*+ types (grid shapes). +*/
33
34 /*+ The size of the grids +*/
35 int widths [NLEVELS]={ 8 ,16 ,30}, /*+ width in tiles. +*/
36 heights[NLEVELS]={ 8 ,16 ,16}, /*+ height in tiles. +*/
37 nbombs [NLEVELS]={10 ,40 ,99}; /*+ number of bombs. +*/
38
39 /*+ The current game status +*/
40 static int status=GAME_WAIT;
41
42 /*+ The current information about the grid +*/
43 int grid_width, /*+ width. +*/
44 grid_height, /*+ height. +*/
45 grid_bombs, /*+ number of bombs. +*/
46 grid_level, /*+ level. +*/
47 grid_type; /*+ type. +*/
48
49 /*+ The status of the tiles in the grid. +*/
50 static unsigned char state[MAX_SIZE][MAX_SIZE];
51
52 /*+ The status of the game, +*/
53 static int n_unseen, /*+ the number of tiles unseen. +*/
54 n_think, /*+ the number of flags put down. +*/
55 ticks; /*+ The number of milliseconds of time passed. +*/
56
57
58 static int count_adjacent(int x,int y,int flag);
59
60
61 /*++++++++++++++++++++++++++++++++++++++
62 The main program.
63
64 int argc The command line parameters.
65
66 char** argv The command line parameters.
67 ++++++++++++++++++++++++++++++++++++++*/
68
main(int argc,char ** argv)69 int main(int argc,char** argv)
70 {
71 int level=GAME_EASY,type=GAME_SQUARE; /* Default values. */
72 int xstatus=0;
73 long t;
74
75 printf("\n"
76 "XBomb Version 2.2b\n"
77 "\n"
78 "(c) Andrew M. Bishop 1994-2013 [http://www.gedanken.org.uk/software/xbomb/]\n"
79 "\n");
80
81 if(argc>1)
82 {
83 int i,j;
84
85 for(j=0;j<NLEVELS;j++)
86 {
87 char levstr[3];
88 sprintf(levstr,"-%1d",j+1);
89 for(i=1;i<argc;i++)
90 if(!strcmp(levstr,argv[i]))
91 level=GAME_LEVEL+j;
92 }
93
94 for(j=0;j<NTYPES;j++)
95 {
96 for(i=1;i<argc;i++)
97 if(argv[i][0]=='-' && !strncasecmp(types[j],argv[i]+1,strlen(argv[i])-1))
98 type=GAME_TYPE+j;
99 }
100
101 for(i=1;i<argc;i++)
102 if(strlen(argv[i])>2 && !strncmp("-hiscore",argv[i],strlen(argv[i])))
103 {
104 grid_type=type;
105 PrintHighScores();
106 exit(0);
107 }
108 }
109
110 t=time(&t);
111
112 srand((unsigned int)t);
113
114 InitialiseX(&argc,argv);
115
116 StartGame(level,type);
117 ShowHighScores();
118
119 for(;;)
120 {
121 xstatus=ProcessXEvents();
122
123 if(xstatus==GAME_QUIT)
124 break;
125
126 if(xstatus>=GAME_LEVEL && xstatus<(GAME_LEVEL+NLEVELS) && xstatus!=level)
127 {
128 level=xstatus;
129 xstatus=GAME_START;
130 }
131
132 if(xstatus>=GAME_TYPE && xstatus<(GAME_TYPE+NTYPES) && xstatus!=type)
133 {
134 type=xstatus;
135 xstatus=GAME_START;
136 }
137
138 if(xstatus==GAME_START)
139 {
140 if(status==GAME_CONTINUES)
141 StopGame();
142 StartGame(level,type);
143 ShowHighScores();
144 }
145
146 if(status==GAME_WON)
147 {
148 StopGame();
149 AddHighScore(ticks);
150 ShowHighScores();
151 }
152 else if(status==GAME_LOST)
153 {
154 StopGame();
155 ShowHighScores();
156 }
157 }
158
159 FinishUpX();
160
161 return(0);
162 }
163
164
165 /*++++++++++++++++++++++++++++++++++++++
166 Start a new game.
167
168 int level The level of the game to play.
169
170 int type The type of game to play.
171 ++++++++++++++++++++++++++++++++++++++*/
172
StartGame(int level,int type)173 void StartGame(int level,int type)
174 {
175 int x,y;
176
177 grid_type=type;
178 grid_level=level;
179 grid_width=widths[level-GAME_LEVEL];
180 grid_height=heights[level-GAME_LEVEL];
181 grid_bombs=nbombs[level-GAME_LEVEL];
182
183 n_unseen=grid_width*grid_height;
184 n_think=0;
185
186 status=GAME_READY;
187
188 for(x=0;x<grid_width;x++)
189 for(y=0;y<grid_height;y++)
190 state[x][y]=UNSEEN;
191
192 ScaleWindow();
193 DrawGrid();
194 StartClock(0);
195 SetUXB(grid_bombs);
196 }
197
198
199 /*++++++++++++++++++++++++++++++++++++++
200 Hides the bombs in the grid.
201
202 int xs The location that has no neighbouring bombs.
203
204 int ys The location that has no neighbouring bombs.
205 ++++++++++++++++++++++++++++++++++++++*/
206
HideBombs(int xs,int ys)207 void HideBombs(int xs,int ys)
208 {
209 int i,x,y;
210
211 state[xs][ys]+=CORRECT;
212
213 for(i=0;i<grid_bombs;i++)
214 {
215 do{
216 x=(rand()>>8)%grid_width;
217 y=(rand()>>8)%grid_height;
218 }
219 while(state[x][y]!=UNSEEN || count_adjacent(x,y,CORRECT));
220
221 state[x][y]+=ACTUAL_BOMB;
222 }
223
224 state[xs][ys]-=CORRECT;
225
226 for(x=0;x<grid_width;x++)
227 for(y=0;y<grid_height;y++)
228 if(!(state[x][y]&ACTUAL_BOMB))
229 state[x][y]+=count_adjacent(x,y,ACTUAL_BOMB);
230
231 StartClock(1);
232 }
233
234
235 /*++++++++++++++++++++++++++++++++++++++
236 Stop the game.
237 ++++++++++++++++++++++++++++++++++++++*/
238
StopGame(void)239 void StopGame(void)
240 {
241 status=GAME_WAIT;
242 ticks=StopClock();
243 }
244
245
246 /*++++++++++++++++++++++++++++++++++++++
247 Draws the grid at the start of the game or for expose events.
248 ++++++++++++++++++++++++++++++++++++++*/
249
DrawGrid(void)250 void DrawGrid(void)
251 {
252 int x,y;
253
254 for(x=0;x<grid_width;x++)
255 for(y=0;y<grid_height;y++)
256 DrawSquare(x,y,state[x][y]);
257 }
258
259
260 /*++++++++++++++++++++++++++++++++++++++
261 Select a single square and clear it or explode the bomb.
262
263 int x The location of the square.
264
265 int y The location of the square.
266 ++++++++++++++++++++++++++++++++++++++*/
267
SelectSquare(int x,int y)268 void SelectSquare(int x,int y)
269 {
270 if(!(state[x][y]&UNSEEN) || state[x][y]&THINK_BOMB || status==GAME_WAIT)
271 return;
272
273 if(status==GAME_READY)
274 {
275 HideBombs(x,y);
276 status=GAME_CONTINUES;
277 }
278
279 RemoveEmpties(x,y);
280
281 if(state[x][y]&ACTUAL_BOMB)
282 {
283 int x,y;
284
285 for(x=0;x<grid_width;x++)
286 for(y=0;y<grid_width;y++)
287 if(state[x][y]&UNSEEN)
288 {
289 state[x][y]&=~UNSEEN;
290 if(state[x][y]&THINK_BOMB && state[x][y]&ACTUAL_BOMB)
291 state[x][y]|=CORRECT;
292 }
293 else
294 state[x][y]|=CORRECT;
295
296 status=GAME_LOST;
297 DrawGrid();
298 }
299 else
300 {
301 DrawSquare(x,y,state[x][y]);
302
303 if(n_think==n_unseen)
304 status=GAME_WON;
305 }
306 }
307
308
309 /*++++++++++++++++++++++++++++++++++++++
310 Select a single square and clear it, explode the bomb or clear the adjacent.
311
312 int x The location of the square.
313
314 int y The location of the square.
315 ++++++++++++++++++++++++++++++++++++++*/
316
SelectSquareOrAdjacent(int x,int y)317 void SelectSquareOrAdjacent(int x,int y)
318 {
319 if(state[x][y]&UNSEEN)
320 SelectSquare(x,y);
321 else /* if(state[x][y]&EMPTY) */
322 SelectAdjacent(x,y);
323 }
324
325
326 /*++++++++++++++++++++++++++++++++++++++
327 Select all of the squares adjacent
328
329 int x The location of the square.
330
331 int y The location of the square.
332 ++++++++++++++++++++++++++++++++++++++*/
333
SelectAdjacent(int x,int y)334 void SelectAdjacent(int x,int y)
335 {
336 int dx,dy,n,dd=(grid_type==GAME_TRIANGLE?2:1);
337
338 if(state[x][y]&UNSEEN || status==GAME_WAIT)
339 return;
340
341 n=count_adjacent(x,y,THINK_BOMB);
342
343 if(state[x][y]!=n)
344 return;
345
346 for(dx=-dd;dx<=dd;dx++)
347 {
348 if((x+dx)<0 || (x+dx)>=grid_width)
349 continue;
350
351 for(dy=-1;dy<=1;dy++)
352 {
353 if((y+dy)<0 || (y+dy)>=grid_height)
354 continue;
355 if(dx==0 && dy==0)
356 continue;
357
358 if(grid_type==GAME_HEXAGON && dy!=0 && dx==(1-2*(y%2)))
359 continue;
360 if(grid_type==GAME_TRIANGLE && dy==(2*((x+y)%2)-1) && (dx==-2 || dx==2))
361 continue;
362
363 if(!(state[x+dx][y+dy]&THINK_BOMB))
364 SelectSquare(x+dx,y+dy);
365 }
366 }
367 }
368
369
370 /*++++++++++++++++++++++++++++++++++++++
371 Marks as a bomb the square.
372
373 int x The location of the square.
374
375 int y The location of the square.
376 ++++++++++++++++++++++++++++++++++++++*/
377
MarkBomb(int x,int y)378 void MarkBomb(int x,int y)
379 {
380 if(!(state[x][y]&UNSEEN) || status==GAME_WAIT)
381 return;
382
383 if(n_think==grid_bombs && !(state[x][y]&THINK_BOMB))
384 return;
385
386 state[x][y]^=THINK_BOMB;
387
388 if(state[x][y]&THINK_BOMB)
389 n_think++;
390 else
391 n_think--;
392
393 SetUXB(grid_bombs-n_think);
394
395 DrawSquare(x,y,state[x][y]);
396
397 if(n_think==n_unseen)
398 status=GAME_WON;
399 }
400
401
402 /*++++++++++++++++++++++++++++++++++++++
403 Removes the empty squares around here
404
405 int x Start from this position.
406
407 int y Start from this position.
408 ++++++++++++++++++++++++++++++++++++++*/
409
RemoveEmpties(int x,int y)410 void RemoveEmpties(int x, int y)
411 {
412 int dx,dy,dd=(grid_type==GAME_TRIANGLE?2:1);
413
414 if(!(state[x][y]&UNSEEN) || state[x][y]&THINK_BOMB)
415 return;
416
417 state[x][y]&=~UNSEEN;
418 n_unseen--;
419 DrawSquare(x,y,state[x][y]);
420
421 if(state[x][y]!=EMPTY)
422 return;
423
424 for(dx=-dd;dx<=dd;dx++)
425 {
426 if((x+dx)<0 || (x+dx)>=grid_width)
427 continue;
428
429 for(dy=-1;dy<=1;dy++)
430 {
431 if((y+dy)<0 || (y+dy)>=grid_height)
432 continue;
433 if(dx==0 && dy==0)
434 continue;
435
436 if(grid_type==GAME_HEXAGON && dy!=0 && dx==(1-2*(y%2)))
437 continue;
438 if(grid_type==GAME_TRIANGLE && dy==(2*((x+y)%2)-1) && (dx==-2 || dx==2))
439 continue;
440
441 RemoveEmpties(x+dx,y+dy);
442 }
443 }
444 }
445
446
447 /*++++++++++++++++++++++++++++++++++++++
448 Count the number of adjacent squares that are bombs.
449
450 int count_adjacent Returns the number of adjacent tiles with the specified flag.
451
452 int x The position to start from.
453
454 int y The position to start from.
455
456 int flag The type of thing to look for.
457 ++++++++++++++++++++++++++++++++++++++*/
458
count_adjacent(int x,int y,int flag)459 static int count_adjacent(int x,int y,int flag)
460 {
461 int n=0,dx,dy,dd=(grid_type==GAME_TRIANGLE?2:1);
462
463 for(dx=-dd;dx<=dd;dx++)
464 {
465 if((x+dx)<0 || (x+dx)>=grid_width)
466 continue;
467
468 for(dy=-1;dy<=1;dy++)
469 {
470 if((y+dy)<0 || (y+dy)>=grid_height)
471 continue;
472
473 if(grid_type==GAME_HEXAGON && dy!=0 && dx==(1-2*(y%2)))
474 continue;
475 if(grid_type==GAME_TRIANGLE && dy==(2*((x+y)%2)-1) && (dx==-2 || dx==2))
476 continue;
477
478 if(state[x+dx][y+dy]&flag)
479 n++;
480 }
481 }
482
483 return(n);
484 }
485