1 #include <assert.h>
2 #include <stdlib.h>
3 #include <time.h>
4 #include "merge.h"
5 #include "engine.h"
6 #include "highscore.h"
7
8 /* Utilize block counter to improve some of the functions so they can run
9 * quicker */
10
11 /* This function will move all blocks in the given game the given direction.
12 * The callback function is called after each single move. It can be used to
13 * animate the movement of the board. */
gravitate(struct gfx_state * s,struct gamestate * g,int d,void (* callback)(struct gfx_state * s,struct gamestate * g))14 static void gravitate(struct gfx_state *s, struct gamestate *g, int d, void (*callback)(struct gfx_state *s, struct gamestate *g))
15 {
16
17 #define swap_if_space(xoff, yoff)\
18 do {\
19 if (g->grid[x][y] == 0 && g->grid[x+xoff][y+yoff] != 0) {\
20 g->grid[x][y] = g->grid[x+xoff][y+yoff];\
21 g->grid[x+xoff][y+yoff] = 0;\
22 done = 0;\
23 g->moved = 1;\
24 }\
25 } while (0)
26
27 int x, y;
28 int done = 0;
29
30 if (d == dir_left) {
31 while (!done) {
32 done = 1;
33 for (x = 0; x < g->opts->grid_width - 1; ++x) {
34 for (y = 0; y < g->opts->grid_height; ++y) {
35 swap_if_space(1, 0);
36 }
37 }
38 if (callback)
39 callback(s, g);
40 }
41 }
42 else if (d == dir_right) {
43 while (!done) {
44 done = 1;
45 for (x = g->opts->grid_width - 1; x > 0; --x) {
46 for (y = 0; y < g->opts->grid_height; ++y) {
47 swap_if_space(-1, 0);
48 }
49 }
50 if (callback)
51 callback(s, g);
52 }
53 }
54 else if (d == dir_down) {
55 while (!done) {
56 done = 1;
57 for (y = g->opts->grid_height - 1; y > 0; --y) {
58 for (x = 0; x < g->opts->grid_width; ++x) {
59 swap_if_space(0, -1);
60 }
61 }
62 if (callback)
63 callback(s, g);
64 }
65 }
66 else if (d == dir_up) {
67 while (!done) {
68 done = 1;
69 for (y = 0; y < g->opts->grid_height - 1; ++y) {
70 for (x = 0; x < g->opts->grid_width; ++x) {
71 swap_if_space(0, 1);
72 }
73 }
74 if (callback)
75 callback(s, g);
76 }
77 }
78 else {
79 fatal("Invalid direction passed to gravitate()");
80 /* Not reached */
81 }
82
83 #undef swap_if_space
84 }
85
86 /* The merge function will combine adjacent blocks with the same value for
87 * the given direction. Note, left and right merges will merge in a different
88 * order, so they are not identical in all cases.
89 *
90 * Consider 2 2 2 0
91 *
92 * Right merging: 4 0 2 0
93 * Left merging: 2 0 4 0
94 */
merge(struct gfx_state * s,struct gamestate * g,int d,void (* callback)(struct gfx_state * s,struct gamestate * g))95 static void merge(struct gfx_state *s, struct gamestate *g, int d, void (*callback)(struct gfx_state *s, struct gamestate *g))
96 {
97
98 #define merge_if_equal(xoff, yoff)\
99 do {\
100 if (g->grid[x][y] && (merge_possible(g->grid[x][y], g->grid[x+xoff][y+yoff]))) {\
101 g->grid[x][y] = merge_result(g->grid[x][y], g->grid[x+xoff][y+yoff]);\
102 g->grid[x+xoff][y+yoff] = 0;\
103 g->blocks_in_play -= 1;\
104 g->score_last += merge_value(g->grid[x][y]);\
105 g->score += merge_value(g->grid[x][y]);\
106 g->moved = 1;\
107 }\
108 } while (0)
109
110 int x, y;
111 g->score_last = 0;
112
113 if (d == dir_left) {
114 for (x = 0; x < g->opts->grid_width - 1; ++x) {
115 for (y = 0; y < g->opts->grid_height; ++y) {
116 merge_if_equal(1, 0);
117 }
118 }
119 }
120 else if (d == dir_right) {
121 for (x = g->opts->grid_width - 1; x > 0; --x) {
122 for (y = 0; y < g->opts->grid_height; ++y) {
123 merge_if_equal(-1, 0);
124 }
125 }
126 }
127 else if (d == dir_down) {
128 for (y = g->opts->grid_height - 1; y > 0; --y) {
129 for (x = 0; x < g->opts->grid_width; ++x) {
130 merge_if_equal(0, -1);
131 }
132 }
133 }
134 else if (d == dir_up) {
135 for (y = 0; y < g->opts->grid_height - 1; ++y) {
136 for (x = 0; x < g->opts->grid_width; ++x) {
137 merge_if_equal(0, 1);
138 }
139 }
140 }
141 else {
142 fatal("Invalid direction passed to merge()");
143 /* Not reached */
144 }
145
146 if (callback)
147 callback(s, g);
148
149 #undef merge_if_equal
150 }
151
152 /* Scan the current board and determine if an end outcome has been reached.
153 * -1 indicates a lose condition, 1 indicates a win condition, 0 indicates
154 * end has not yet been reached. */
gamestate_end_condition(struct gamestate * g)155 int gamestate_end_condition(struct gamestate *g)
156 {
157 int ret = -1;
158 //size_t blocks_counted = 0;
159 int x, y;
160
161 for (x = 0; x < g->opts->grid_width; ++x) {
162 for (y = 0; y < g->opts->grid_height; ++y) {
163 if (g->grid[x][y] == merge_goal())
164 return 1;
165 if (!g->grid[x][y] || ((x + 1 < g->opts->grid_width) &&
166 merge_possible(g->grid[x][y], g->grid[x+1][y]))
167 || ((y + 1 < g->opts->grid_height) &&
168 merge_possible(g->grid[x][y], g->grid[x][y+1])))
169 ret = 0;
170 }
171 }
172
173 return ret;
174 }
175
176 /* Place a random block into the current grid */
gamestate_new_block(struct gamestate * g)177 void gamestate_new_block(struct gamestate *g)
178 {
179 /* Exit early if there are no spaces to place a block */
180 if (g->blocks_in_play >= g->gridsize) return;
181
182 int block_number = rand() % (g->gridsize - g->blocks_in_play);
183
184 int x, y, p = 0;
185 for (y = 0; y < g->opts->grid_height; ++y) {
186 for (x = 0; x < g->opts->grid_width; ++x) {
187 if (!g->grid[x][y]) {
188 if (p == block_number) {
189 g->grid[x][y] = rand() & 3 ? 1 : 2;
190 g->blocks_in_play += 1;
191 return;
192 }
193 else {
194 ++p;
195 }
196 }
197 }
198 }
199
200 /* This should never be reached; but just in case */
201 assert(0);
202 }
203
204 /* This returns the number of digits in the base10 rep of n. The ceiling is
205 * taken so this will be one greater than required */
digits_ceiling(unsigned int n)206 static int digits_ceiling(unsigned int n)
207 {
208 int l = 0;
209 while (n) n /= 10, ++l;
210 return l + 1;
211 }
212
213 /* Return NULL if we couldn't allocate space for the gamestate. initializating the
214 * gamestate will parse the options internally, so any caller should pass argc and argv
215 * through this function */
gamestate_init(int argc,char ** argv)216 struct gamestate* gamestate_init(int argc, char **argv)
217 {
218 struct gameoptions *opt = gameoptions_default();
219 if (argc != 0) parse_options(opt, argc, argv);
220
221 if (!opt) return NULL;
222
223 srand(time(NULL));
224
225 struct gamestate *g = malloc(sizeof(struct gamestate));
226 if (!g) goto gamestate_alloc_fail;
227 g->gridsize = opt->grid_width * opt->grid_height;
228
229 g->grid_data_ptr = calloc(g->gridsize, sizeof(int));
230 if (!g->grid_data_ptr) goto grid_data_alloc_fail;
231
232 g->grid = malloc(opt->grid_height * sizeof(int*));
233 if (!g->grid) goto grid_alloc_fail;
234
235 /* Switch to two allocation version */
236 int i;
237 int **iterator = g->grid;
238 for (i = 0; i < g->gridsize; i += opt->grid_width)
239 *iterator++ = &g->grid_data_ptr[i];
240
241 g->moved = 0;
242 g->score = 0;
243 g->score_high = 0;
244 g->score_last = 0;
245 g->print_width = digits_ceiling(merge_value(merge_goal()));
246 g->blocks_in_play = 0;
247 g->opts = opt;
248
249 /* Clamp spawn rate to maximum to avoid possible excessive calculation
250 * int generation of blocks */
251 if (g->opts->spawn_rate > g->gridsize)
252 g->opts->spawn_rate = g->gridsize;
253
254 highscore_load(g);
255
256 /* Initial 3 random blocks */
257 gamestate_new_block(g);
258 gamestate_new_block(g);
259 gamestate_new_block(g);
260 return g;
261
262 grid_alloc_fail:
263 free(g->grid_data_ptr);
264 grid_data_alloc_fail:
265 free(g);
266 gamestate_alloc_fail:
267 return NULL;
268 }
269
270 /* A tick is a gravitate, merge then gravitate all in the same direction.
271 * the moved variable is set to 0 initially and if the gravitate of merge
272 * functions modify it, we can determine which action to take. */
gamestate_tick(struct gfx_state * s,struct gamestate * g,int d,void (* callback)(struct gfx_state *,struct gamestate *))273 int gamestate_tick(struct gfx_state *s, struct gamestate *g, int d, void (*callback)(struct gfx_state*, struct gamestate*))
274 {
275 /* Reset move. Altered by gravitate and merge if we do move */
276 g->moved = 0;
277 gravitate(s, g, d, callback);
278 merge(s, g, d, callback);
279 gravitate(s, g, d, callback);
280 return g->moved;
281 }
282
283 /* Free all data associated with the gamestate */
gamestate_clear(struct gamestate * g)284 void gamestate_clear(struct gamestate *g)
285 {
286 highscore_save(g);
287 gameoptions_destroy(g->opts);
288 free(g->grid_data_ptr); /* Free grid data */
289 free(g->grid); /* Free pointers to data slots */
290 free(g);
291 }
292