-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgame.c
237 lines (208 loc) · 7.14 KB
/
game.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*--------------------------------------------------------------------*\
| This file is part of jmines
|
| Game engine
|
| Copyright 2012, Jeremy Brubaker <[email protected]>
|---------------------------------------------------------------------*\
| jmines is free software: you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| jmines is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with jmines. If not, see <http://www.gnu.org/licenses/>. |
\*--------------------------------------------------------------------*/
#include <stdlib.h>
#include <time.h>
#include "game.h"
/*
* Internal engine functions
*/
static int mark_cell (game_data *, int, int, cell_marker);
static int rand_limit (int);
/*
* Initialize a game data structure and return a pointer
*/
game_data *
init_game (int num_mines, int height, int width)
{
int i = 0; /* Counters */
int x = 0;
int y = 0;
game_data *data = (game_data *)malloc (sizeof (game_data));
if (!data)
return NULL;
data->num_mines = num_mines; /* User requested number of mines */
data->num_flags = 0; /* No flags have been placed */
data->grid.height = (height > MAX_Y) ? MAX_Y : height; /* User requested dimensions */
data->grid.width = (width > MAX_X) ? MAX_X : width;
/* Set each cell to empty, covered and free of markers */
for (x = 0; x < MAX_X+2; x++) /* MAX_* + 2 in order to handle
one cell padding of grid */
{
for (y = 0; y < MAX_Y+2; y++)
{
data->grid.cell[x][y].is_covered = true;
data->grid.cell[x][y].contents = EMPTY;
data->grid.cell[x][y].marker = NONE;
}
}
/* Initialize rand () */
srand ((unsigned)time (NULL));
/* Place num_mines mines in the grid
*
* width-1, height-1, x+1 and y+1 are there
* to handle the one cell padding of the grid */
for (i = 0; i < num_mines;)
{
x = rand_limit (width-1); /* Randomly generate coordinates */
y = rand_limit (height-1);
/* Only place a mine if the cell is currently empty */
if (data->grid.cell[x+1][y+1].contents == EMPTY)
{
data->grid.cell[x+1][y+1].contents = MINE;
i++;
}
}
/* Set all cell adjacent to a mine to the proper number
*
* x=1, y=1 and <= data->grid.[height|width] are there
* to handle the one cell padding of the grid */
for (x = 1; x <= data->grid.height; x++)
{
for (y = 1; y <= data->grid.width; y++)
{
/* Next iteration if cell contains a mine */
if (data->grid.cell[x][y].contents == MINE)
continue;
/* Check each adjacent cell and increment contents if it contains a
* mine. Grid padding cells are always empty */
if (data->grid.cell[x ][y-1].contents == MINE) data->grid.cell[x][y].contents++;
if (data->grid.cell[x ][y+1].contents == MINE) data->grid.cell[x][y].contents++;
if (data->grid.cell[x-1][y-1].contents == MINE) data->grid.cell[x][y].contents++;
if (data->grid.cell[x-1][y ].contents == MINE) data->grid.cell[x][y].contents++;
if (data->grid.cell[x-1][y+1].contents == MINE) data->grid.cell[x][y].contents++;
if (data->grid.cell[x+1][y-1].contents == MINE) data->grid.cell[x][y].contents++;
if (data->grid.cell[x+1][y ].contents == MINE) data->grid.cell[x][y].contents++;
if (data->grid.cell[x+1][y+1].contents == MINE) data->grid.cell[x][y].contents++;
}
}
return data;
}
/*
* Flag a cell as containing a mine
*/
int
flag_cell (game_data *data, int x, int y)
{
(void) mark_cell (data, x, y, FLAG);
/* If cell *actually* contains a mine,
* increment counter */
if (data->grid.cell[x][y].contents == MINE)
{
data->num_flags++;
/* If all mines have been flagged,
* declare a winner */
if (data->num_flags == data->num_mines)
return WINNER;
}
return SUCCESS;
}
/*
* Mark a cell as a guess
*/
int
guess_cell (game_data *data, int x, int y)
{
return mark_cell (data, x, y, GUESS);
}
/*
* Remove all markings from a cell
*/
int
unmark_cell (game_data *data, int x, int y)
{
/* If cell was properly marked as a mine,
* decrement the counter */
if (data->grid.cell[x][y].marker == FLAG)
data->num_flags--;
return mark_cell (data, x, y, NONE);
}
/*
* Perform actual marking/unmarking of a cel
*/
int
mark_cell (game_data *data, int x, int y, cell_marker marker)
{
/* Don't mark an uncovered cell */
if (data->grid.cell[x][y].is_covered == false)
return SUCCESS;
return data->grid.cell[x][y].marker = marker;
}
/* Uncover the selected cell
*
* Return LOSER if a mine is uncovered,
* SUCCESS otherwise
*
* Otherwise do a flood fill uncover, stopping
* at ONE, TWO, THREE, etc.
*/
int
uncover_cell (game_data *data, int x, int y)
{
/* Return if we hit the grid boundaries */
if ((x == 0) ||
(y == 0) ||
(x == data->grid.width+1) ||
(y == data->grid.height+1))
return SUCCESS;
/* Do nothing if we try to uncover an already uncovered cell */
if (data->grid.cell[x][y].is_covered == false)
{
;
}
else if (data->grid.cell[x][y].contents == MINE)
{
return LOSER; /* Return LOSER if we somehow uncovered a MINE */
}
else if (data->grid.cell[x][y].contents != EMPTY)
{
data->grid.cell[x][y].is_covered = false; /* Uncover non-empty,
non-mine cells */
}
else
{
/* Cell is empty so, uncover it and uncover all
* adjacent cells */
data->grid.cell[x][y].is_covered = false;
(void) uncover_cell (data, x-1, y-1);
(void) uncover_cell (data, x-1, y);
(void) uncover_cell (data, x-1, y+1);
(void) uncover_cell (data, x , y-1);
(void) uncover_cell (data, x , y+1);
(void) uncover_cell (data, x+1, y-1);
(void) uncover_cell (data, x+1, y);
(void) uncover_cell (data, x+1, y+1);
}
return SUCCESS;
}
/* TODO: Need a function to handle "uncovering" an already uncovered number
* TODO: like middle clicking in Windows Minesweeper */
/*
* Generate a random number from 0 to limit (inclusiv) */
int
rand_limit (int limit)
{
int divisor = RAND_MAX / (limit + 1);
int retval = 0;
do {
retval = rand () / divisor;
} while (retval > limit);
return retval;
}