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