diff options
Diffstat (limited to 'apps/plugins/puzzles/undead.c')
-rw-r--r-- | apps/plugins/puzzles/undead.c | 2738 |
1 files changed, 2738 insertions, 0 deletions
diff --git a/apps/plugins/puzzles/undead.c b/apps/plugins/puzzles/undead.c new file mode 100644 index 0000000000..28fd0f35fe --- /dev/null +++ b/apps/plugins/puzzles/undead.c | |||
@@ -0,0 +1,2738 @@ | |||
1 | /* | ||
2 | * undead: Implementation of Haunted Mirror Mazes | ||
3 | * | ||
4 | * http://www.janko.at/Raetsel/Spukschloss/index.htm | ||
5 | * | ||
6 | * Puzzle definition is the total number of each monster type, the | ||
7 | * grid definition, and the list of sightings (clockwise, starting | ||
8 | * from top left corner) | ||
9 | * | ||
10 | * Example: (Janko puzzle No. 1, | ||
11 | * http://www.janko.at/Raetsel/Spukschloss/001.a.htm ) | ||
12 | * | ||
13 | * Ghosts: 0 Vampires: 2 Zombies: 6 | ||
14 | * | ||
15 | * 2 1 1 1 | ||
16 | * 1 \ \ . / 2 | ||
17 | * 0 \ . / . 2 | ||
18 | * 0 / . / . 2 | ||
19 | * 3 . . . \ 2 | ||
20 | * 3 3 2 2 | ||
21 | * | ||
22 | * would be encoded into: | ||
23 | * 4x4:0,2,6,LLaRLaRaRaRdL,2,1,1,1,2,2,2,2,2,2,3,3,3,0,0,1 | ||
24 | * | ||
25 | * Additionally, the game description can contain monsters fixed at a | ||
26 | * certain grid position. The internal generator does not (yet) use | ||
27 | * this feature, but this is needed to enter puzzles like Janko No. | ||
28 | * 14, which is encoded as: | ||
29 | * 8x5:12,12,0,LaRbLaRaLaRLbRaVaVaGRaRaRaLbLaRbRLb,0,2,0,2,2,1,2,1,3,1,0,1,8,4,3,0,0,2,3,2,7,2,1,6,2,1 | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | #include <stdio.h> | ||
34 | #include <stdlib.h> | ||
35 | #include <string.h> | ||
36 | #include "rbassert.h" | ||
37 | #include <ctype.h> | ||
38 | #include <math.h> | ||
39 | |||
40 | #include "puzzles.h" | ||
41 | |||
42 | enum { | ||
43 | COL_BACKGROUND, | ||
44 | COL_GRID, | ||
45 | COL_TEXT, | ||
46 | COL_ERROR, | ||
47 | COL_HIGHLIGHT, | ||
48 | COL_FLASH, | ||
49 | COL_GHOST, | ||
50 | COL_ZOMBIE, | ||
51 | COL_VAMPIRE, | ||
52 | COL_DONE, | ||
53 | NCOLOURS | ||
54 | }; | ||
55 | |||
56 | #define DIFFLIST(A) \ | ||
57 | A(EASY,Easy,e) \ | ||
58 | A(NORMAL,Normal,n) \ | ||
59 | A(TRICKY,Tricky,t) | ||
60 | #define ENUM(upper,title,lower) DIFF_ ## upper, | ||
61 | #define TITLE(upper,title,lower) #title, | ||
62 | #define ENCODE(upper,title,lower) #lower | ||
63 | #define CONFIG(upper,title,lower) ":" #title | ||
64 | enum { DIFFLIST(ENUM) DIFFCOUNT }; | ||
65 | static char const *const undead_diffnames[] = { DIFFLIST(TITLE) "(count)" }; | ||
66 | static char const undead_diffchars[] = DIFFLIST(ENCODE); | ||
67 | #define DIFFCONFIG DIFFLIST(CONFIG) | ||
68 | |||
69 | struct game_params { | ||
70 | int w; /* Grid width */ | ||
71 | int h; /* Grid height */ | ||
72 | int diff; /* Puzzle difficulty */ | ||
73 | }; | ||
74 | |||
75 | static const struct game_params undead_presets[] = { | ||
76 | { 4, 4, DIFF_EASY }, | ||
77 | { 4, 4, DIFF_NORMAL }, | ||
78 | { 4, 4, DIFF_TRICKY }, | ||
79 | { 5, 5, DIFF_EASY }, | ||
80 | { 5, 5, DIFF_NORMAL }, | ||
81 | { 5, 5, DIFF_TRICKY }, | ||
82 | { 7, 7, DIFF_EASY }, | ||
83 | { 7, 7, DIFF_NORMAL } | ||
84 | }; | ||
85 | |||
86 | #define DEFAULT_PRESET 1 | ||
87 | |||
88 | static game_params *default_params(void) { | ||
89 | game_params *ret = snew(game_params); | ||
90 | |||
91 | *ret = undead_presets[DEFAULT_PRESET]; | ||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | static int game_fetch_preset(int i, char **name, game_params **params) { | ||
96 | game_params *ret; | ||
97 | char buf[64]; | ||
98 | |||
99 | if (i < 0 || i >= lenof(undead_presets)) return FALSE; | ||
100 | |||
101 | ret = default_params(); | ||
102 | *ret = undead_presets[i]; /* struct copy */ | ||
103 | *params = ret; | ||
104 | |||
105 | sprintf(buf, "%dx%d %s", | ||
106 | undead_presets[i].w, undead_presets[i].h, | ||
107 | undead_diffnames[undead_presets[i].diff]); | ||
108 | *name = dupstr(buf); | ||
109 | |||
110 | return TRUE; | ||
111 | } | ||
112 | |||
113 | static void free_params(game_params *params) { | ||
114 | sfree(params); | ||
115 | } | ||
116 | |||
117 | static game_params *dup_params(const game_params *params) | ||
118 | { | ||
119 | game_params *ret = snew(game_params); | ||
120 | *ret = *params; /* structure copy */ | ||
121 | return ret; | ||
122 | } | ||
123 | |||
124 | static void decode_params(game_params *params, char const *string) { | ||
125 | params->w = params->h = atoi(string); | ||
126 | |||
127 | while (*string && isdigit((unsigned char) *string)) ++string; | ||
128 | if (*string == 'x') { | ||
129 | string++; | ||
130 | params->h = atoi(string); | ||
131 | while (*string && isdigit((unsigned char)*string)) string++; | ||
132 | } | ||
133 | |||
134 | params->diff = DIFF_NORMAL; | ||
135 | if (*string == 'd') { | ||
136 | int i; | ||
137 | string++; | ||
138 | for (i = 0; i < DIFFCOUNT; i++) | ||
139 | if (*string == undead_diffchars[i]) | ||
140 | params->diff = i; | ||
141 | if (*string) string++; | ||
142 | } | ||
143 | |||
144 | return; | ||
145 | } | ||
146 | |||
147 | static char *encode_params(const game_params *params, int full) | ||
148 | { | ||
149 | char buf[256]; | ||
150 | sprintf(buf, "%dx%d", params->w, params->h); | ||
151 | if (full) | ||
152 | sprintf(buf + strlen(buf), "d%c", undead_diffchars[params->diff]); | ||
153 | return dupstr(buf); | ||
154 | } | ||
155 | |||
156 | static config_item *game_configure(const game_params *params) | ||
157 | { | ||
158 | config_item *ret; | ||
159 | char buf[64]; | ||
160 | |||
161 | ret = snewn(4, config_item); | ||
162 | |||
163 | ret[0].name = "Width"; | ||
164 | ret[0].type = C_STRING; | ||
165 | sprintf(buf, "%d", params->w); | ||
166 | ret[0].sval = dupstr(buf); | ||
167 | ret[0].ival = 0; | ||
168 | |||
169 | ret[1].name = "Height"; | ||
170 | ret[1].type = C_STRING; | ||
171 | sprintf(buf, "%d", params->h); | ||
172 | ret[1].sval = dupstr(buf); | ||
173 | ret[1].ival = 0; | ||
174 | |||
175 | ret[2].name = "Difficulty"; | ||
176 | ret[2].type = C_CHOICES; | ||
177 | ret[2].sval = DIFFCONFIG; | ||
178 | ret[2].ival = params->diff; | ||
179 | |||
180 | ret[3].name = NULL; | ||
181 | ret[3].type = C_END; | ||
182 | ret[3].sval = NULL; | ||
183 | ret[3].ival = 0; | ||
184 | |||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | static game_params *custom_params(const config_item *cfg) | ||
189 | { | ||
190 | game_params *ret = snew(game_params); | ||
191 | |||
192 | ret->w = atoi(cfg[0].sval); | ||
193 | ret->h = atoi(cfg[1].sval); | ||
194 | ret->diff = cfg[2].ival; | ||
195 | return ret; | ||
196 | } | ||
197 | |||
198 | static char *validate_params(const game_params *params, int full) | ||
199 | { | ||
200 | if ((params->w * params->h ) > 54) return "Grid is too big"; | ||
201 | if (params->w < 3) return "Width must be at least 3"; | ||
202 | if (params->h < 3) return "Height must be at least 3"; | ||
203 | if (params->diff >= DIFFCOUNT) return "Unknown difficulty rating"; | ||
204 | return NULL; | ||
205 | } | ||
206 | |||
207 | /* --------------------------------------------------------------- */ | ||
208 | /* Game state allocation, deallocation. */ | ||
209 | |||
210 | struct path { | ||
211 | int length; | ||
212 | int *p; | ||
213 | int grid_start; | ||
214 | int grid_end; | ||
215 | int num_monsters; | ||
216 | int *mapping; | ||
217 | int sightings_start; | ||
218 | int sightings_end; | ||
219 | int *xy; | ||
220 | }; | ||
221 | |||
222 | struct game_common { | ||
223 | int refcount; | ||
224 | struct game_params params; | ||
225 | int wh; | ||
226 | int num_ghosts,num_vampires,num_zombies,num_total; | ||
227 | int num_paths; | ||
228 | struct path *paths; | ||
229 | int *grid; | ||
230 | int *xinfo; | ||
231 | int *fixed; | ||
232 | int solved; | ||
233 | }; | ||
234 | |||
235 | struct game_state { | ||
236 | struct game_common *common; | ||
237 | int *guess; | ||
238 | unsigned char *pencils; | ||
239 | unsigned char *cell_errors; | ||
240 | unsigned char *hint_errors; | ||
241 | unsigned char *hints_done; | ||
242 | unsigned char count_errors[3]; | ||
243 | int solved; | ||
244 | int cheated; | ||
245 | }; | ||
246 | |||
247 | static game_state *new_state(const game_params *params) { | ||
248 | int i; | ||
249 | game_state *state = snew(game_state); | ||
250 | state->common = snew(struct game_common); | ||
251 | |||
252 | state->common->refcount = 1; | ||
253 | state->common->params.w = params->w; | ||
254 | state->common->params.h = params->h; | ||
255 | state->common->params.diff = params->diff; | ||
256 | |||
257 | state->common->wh = (state->common->params.w +2) * (state->common->params.h +2); | ||
258 | |||
259 | state->common->num_ghosts = 0; | ||
260 | state->common->num_vampires = 0; | ||
261 | state->common->num_zombies = 0; | ||
262 | state->common->num_total = 0; | ||
263 | |||
264 | state->common->grid = snewn(state->common->wh, int); | ||
265 | state->common->xinfo = snewn(state->common->wh, int); | ||
266 | state->common->fixed = NULL; | ||
267 | state->common->solved = FALSE; | ||
268 | |||
269 | state->common->num_paths = | ||
270 | state->common->params.w + state->common->params.h; | ||
271 | state->common->paths = snewn(state->common->num_paths, struct path); | ||
272 | |||
273 | for (i=0;i<state->common->num_paths;i++) { | ||
274 | state->common->paths[i].length = 0; | ||
275 | state->common->paths[i].grid_start = -1; | ||
276 | state->common->paths[i].grid_end = -1; | ||
277 | state->common->paths[i].num_monsters = 0; | ||
278 | state->common->paths[i].sightings_start = 0; | ||
279 | state->common->paths[i].sightings_end = 0; | ||
280 | state->common->paths[i].p = snewn(state->common->wh,int); | ||
281 | state->common->paths[i].xy = snewn(state->common->wh,int); | ||
282 | state->common->paths[i].mapping = snewn(state->common->wh,int); | ||
283 | } | ||
284 | |||
285 | state->guess = NULL; | ||
286 | state->pencils = NULL; | ||
287 | |||
288 | state->cell_errors = snewn(state->common->wh, unsigned char); | ||
289 | for (i=0;i<state->common->wh;i++) | ||
290 | state->cell_errors[i] = FALSE; | ||
291 | state->hint_errors = snewn(2*state->common->num_paths, unsigned char); | ||
292 | for (i=0;i<2*state->common->num_paths;i++) | ||
293 | state->hint_errors[i] = FALSE; | ||
294 | state->hints_done = snewn(2 * state->common->num_paths, unsigned char); | ||
295 | memset(state->hints_done, 0, | ||
296 | 2 * state->common->num_paths * sizeof(unsigned char)); | ||
297 | for (i=0;i<3;i++) | ||
298 | state->count_errors[i] = FALSE; | ||
299 | |||
300 | state->solved = FALSE; | ||
301 | state->cheated = FALSE; | ||
302 | |||
303 | return state; | ||
304 | } | ||
305 | |||
306 | static game_state *dup_game(const game_state *state) | ||
307 | { | ||
308 | game_state *ret = snew(game_state); | ||
309 | |||
310 | ret->common = state->common; | ||
311 | ret->common->refcount++; | ||
312 | |||
313 | if (state->guess != NULL) { | ||
314 | ret->guess = snewn(ret->common->num_total,int); | ||
315 | memcpy(ret->guess, state->guess, ret->common->num_total*sizeof(int)); | ||
316 | } | ||
317 | else ret->guess = NULL; | ||
318 | |||
319 | if (state->pencils != NULL) { | ||
320 | ret->pencils = snewn(ret->common->num_total,unsigned char); | ||
321 | memcpy(ret->pencils, state->pencils, | ||
322 | ret->common->num_total*sizeof(unsigned char)); | ||
323 | } | ||
324 | else ret->pencils = NULL; | ||
325 | |||
326 | if (state->cell_errors != NULL) { | ||
327 | ret->cell_errors = snewn(ret->common->wh,unsigned char); | ||
328 | memcpy(ret->cell_errors, state->cell_errors, | ||
329 | ret->common->wh*sizeof(unsigned char)); | ||
330 | } | ||
331 | else ret->cell_errors = NULL; | ||
332 | |||
333 | if (state->hint_errors != NULL) { | ||
334 | ret->hint_errors = snewn(2*ret->common->num_paths,unsigned char); | ||
335 | memcpy(ret->hint_errors, state->hint_errors, | ||
336 | 2*ret->common->num_paths*sizeof(unsigned char)); | ||
337 | } | ||
338 | else ret->hint_errors = NULL; | ||
339 | |||
340 | if (state->hints_done != NULL) { | ||
341 | ret->hints_done = snewn(2 * state->common->num_paths, unsigned char); | ||
342 | memcpy(ret->hints_done, state->hints_done, | ||
343 | 2 * state->common->num_paths * sizeof(unsigned char)); | ||
344 | } | ||
345 | else ret->hints_done = NULL; | ||
346 | |||
347 | ret->count_errors[0] = state->count_errors[0]; | ||
348 | ret->count_errors[1] = state->count_errors[1]; | ||
349 | ret->count_errors[2] = state->count_errors[2]; | ||
350 | |||
351 | ret->solved = state->solved; | ||
352 | ret->cheated = state->cheated; | ||
353 | |||
354 | return ret; | ||
355 | } | ||
356 | |||
357 | static void free_game(game_state *state) { | ||
358 | int i; | ||
359 | |||
360 | state->common->refcount--; | ||
361 | if (state->common->refcount == 0) { | ||
362 | for (i=0;i<state->common->num_paths;i++) { | ||
363 | sfree(state->common->paths[i].mapping); | ||
364 | sfree(state->common->paths[i].xy); | ||
365 | sfree(state->common->paths[i].p); | ||
366 | } | ||
367 | sfree(state->common->paths); | ||
368 | sfree(state->common->xinfo); | ||
369 | sfree(state->common->grid); | ||
370 | if (state->common->fixed != NULL) sfree(state->common->fixed); | ||
371 | sfree(state->common); | ||
372 | } | ||
373 | if (state->hints_done != NULL) sfree(state->hints_done); | ||
374 | if (state->hint_errors != NULL) sfree(state->hint_errors); | ||
375 | if (state->cell_errors != NULL) sfree(state->cell_errors); | ||
376 | if (state->pencils != NULL) sfree(state->pencils); | ||
377 | if (state->guess != NULL) sfree(state->guess); | ||
378 | sfree(state); | ||
379 | |||
380 | return; | ||
381 | } | ||
382 | |||
383 | /* --------------------------------------------------------------- */ | ||
384 | /* Puzzle generator */ | ||
385 | |||
386 | /* cell states */ | ||
387 | enum { | ||
388 | CELL_EMPTY, | ||
389 | CELL_MIRROR_L, | ||
390 | CELL_MIRROR_R, | ||
391 | CELL_GHOST, | ||
392 | CELL_VAMPIRE, | ||
393 | CELL_ZOMBIE, | ||
394 | CELL_UNDEF | ||
395 | }; | ||
396 | |||
397 | /* grid walk directions */ | ||
398 | enum { | ||
399 | DIRECTION_NONE, | ||
400 | DIRECTION_UP, | ||
401 | DIRECTION_RIGHT, | ||
402 | DIRECTION_LEFT, | ||
403 | DIRECTION_DOWN | ||
404 | }; | ||
405 | |||
406 | int range2grid(int rangeno, int width, int height, int *x, int *y) { | ||
407 | |||
408 | if (rangeno < 0) { | ||
409 | *x = 0; *y = 0; return DIRECTION_NONE; | ||
410 | } | ||
411 | if (rangeno < width) { | ||
412 | *x = rangeno+1; *y = 0; return DIRECTION_DOWN; | ||
413 | } | ||
414 | rangeno = rangeno - width; | ||
415 | if (rangeno < height) { | ||
416 | *x = width+1; *y = rangeno+1; return DIRECTION_LEFT; | ||
417 | } | ||
418 | rangeno = rangeno - height; | ||
419 | if (rangeno < width) { | ||
420 | *x = width-rangeno; *y = height+1; return DIRECTION_UP; | ||
421 | } | ||
422 | rangeno = rangeno - width; | ||
423 | if (rangeno < height) { | ||
424 | *x = 0; *y = height-rangeno; return DIRECTION_RIGHT; | ||
425 | } | ||
426 | *x = 0; *y = 0; | ||
427 | return DIRECTION_NONE; | ||
428 | } | ||
429 | |||
430 | int grid2range(int x, int y, int w, int h) { | ||
431 | if (x>0 && x<w+1 && y>0 && y<h+1) return -1; | ||
432 | if (x<0 || x>w+1 || y<0 || y>h+1) return -1; | ||
433 | if ((x == 0 || x==w+1) && (y==0 || y==h+1)) return -1; | ||
434 | if (y==0) return x-1; | ||
435 | if (x==(w+1)) return y-1+w; | ||
436 | if (y==(h+1)) return 2*w + h - x; | ||
437 | return 2*(w+h) - y; | ||
438 | } | ||
439 | |||
440 | void make_paths(game_state *state) { | ||
441 | int i; | ||
442 | int count = 0; | ||
443 | |||
444 | for (i=0;i<2*(state->common->params.w + state->common->params.h);i++) { | ||
445 | int x,y,dir; | ||
446 | int j,k,num_monsters; | ||
447 | int found; | ||
448 | int c,p; | ||
449 | found = FALSE; | ||
450 | /* Check whether inverse path is already in list */ | ||
451 | for (j=0;j<count;j++) { | ||
452 | if (i == state->common->paths[j].grid_end) { | ||
453 | found = TRUE; | ||
454 | break; | ||
455 | } | ||
456 | } | ||
457 | if (found) continue; | ||
458 | |||
459 | /* We found a new path through the mirror maze */ | ||
460 | state->common->paths[count].grid_start = i; | ||
461 | dir = range2grid(i, state->common->params.w, | ||
462 | state->common->params.h,&x,&y); | ||
463 | state->common->paths[count].sightings_start = | ||
464 | state->common->grid[x+y*(state->common->params.w +2)]; | ||
465 | while (TRUE) { | ||
466 | int c,r; | ||
467 | |||
468 | if (dir == DIRECTION_DOWN) y++; | ||
469 | else if (dir == DIRECTION_LEFT) x--; | ||
470 | else if (dir == DIRECTION_UP) y--; | ||
471 | else if (dir == DIRECTION_RIGHT) x++; | ||
472 | |||
473 | r = grid2range(x, y, state->common->params.w, | ||
474 | state->common->params.h); | ||
475 | if (r != -1) { | ||
476 | state->common->paths[count].grid_end = r; | ||
477 | state->common->paths[count].sightings_end = | ||
478 | state->common->grid[x+y*(state->common->params.w +2)]; | ||
479 | break; | ||
480 | } | ||
481 | |||
482 | c = state->common->grid[x+y*(state->common->params.w+2)]; | ||
483 | state->common->paths[count].xy[state->common->paths[count].length] = | ||
484 | x+y*(state->common->params.w+2); | ||
485 | if (c == CELL_MIRROR_L) { | ||
486 | state->common->paths[count].p[state->common->paths[count].length] = -1; | ||
487 | if (dir == DIRECTION_DOWN) dir = DIRECTION_RIGHT; | ||
488 | else if (dir == DIRECTION_LEFT) dir = DIRECTION_UP; | ||
489 | else if (dir == DIRECTION_UP) dir = DIRECTION_LEFT; | ||
490 | else if (dir == DIRECTION_RIGHT) dir = DIRECTION_DOWN; | ||
491 | } | ||
492 | else if (c == CELL_MIRROR_R) { | ||
493 | state->common->paths[count].p[state->common->paths[count].length] = -1; | ||
494 | if (dir == DIRECTION_DOWN) dir = DIRECTION_LEFT; | ||
495 | else if (dir == DIRECTION_LEFT) dir = DIRECTION_DOWN; | ||
496 | else if (dir == DIRECTION_UP) dir = DIRECTION_RIGHT; | ||
497 | else if (dir == DIRECTION_RIGHT) dir = DIRECTION_UP; | ||
498 | } | ||
499 | else { | ||
500 | state->common->paths[count].p[state->common->paths[count].length] = | ||
501 | state->common->xinfo[x+y*(state->common->params.w+2)]; | ||
502 | } | ||
503 | state->common->paths[count].length++; | ||
504 | } | ||
505 | /* Count unique monster entries in each path */ | ||
506 | state->common->paths[count].num_monsters = 0; | ||
507 | for (j=0;j<state->common->num_total;j++) { | ||
508 | num_monsters = 0; | ||
509 | for (k=0;k<state->common->paths[count].length;k++) | ||
510 | if (state->common->paths[count].p[k] == j) | ||
511 | num_monsters++; | ||
512 | if (num_monsters > 0) | ||
513 | state->common->paths[count].num_monsters++; | ||
514 | } | ||
515 | |||
516 | /* Generate mapping vector */ | ||
517 | c = 0; | ||
518 | for (p=0;p<state->common->paths[count].length;p++) { | ||
519 | int m; | ||
520 | m = state->common->paths[count].p[p]; | ||
521 | if (m == -1) continue; | ||
522 | found = FALSE; | ||
523 | for (j=0; j<c; j++) | ||
524 | if (state->common->paths[count].mapping[j] == m) found = TRUE; | ||
525 | if (!found) state->common->paths[count].mapping[c++] = m; | ||
526 | } | ||
527 | count++; | ||
528 | } | ||
529 | return; | ||
530 | } | ||
531 | |||
532 | struct guess { | ||
533 | int length; | ||
534 | int *guess; | ||
535 | int *possible; | ||
536 | }; | ||
537 | |||
538 | int next_list(struct guess *g, int pos) { | ||
539 | |||
540 | if (pos == 0) { | ||
541 | if ((g->guess[pos] == 1 && g->possible[pos] == 1) || | ||
542 | (g->guess[pos] == 2 && (g->possible[pos] == 3 || | ||
543 | g->possible[pos] == 2)) || | ||
544 | g->guess[pos] == 4) | ||
545 | return FALSE; | ||
546 | if (g->guess[pos] == 1 && (g->possible[pos] == 3 || | ||
547 | g->possible[pos] == 7)) { | ||
548 | g->guess[pos] = 2; return TRUE; | ||
549 | } | ||
550 | if (g->guess[pos] == 1 && g->possible[pos] == 5) { | ||
551 | g->guess[pos] = 4; return TRUE; | ||
552 | } | ||
553 | if (g->guess[pos] == 2 && (g->possible[pos] == 6 || g->possible[pos] == 7)) { | ||
554 | g->guess[pos] = 4; return TRUE; | ||
555 | } | ||
556 | } | ||
557 | |||
558 | if (g->guess[pos] == 1) { | ||
559 | if (g->possible[pos] == 1) { | ||
560 | return next_list(g,pos-1); | ||
561 | } | ||
562 | if (g->possible[pos] == 3 || g->possible[pos] == 7) { | ||
563 | g->guess[pos] = 2; return TRUE; | ||
564 | } | ||
565 | if (g->possible[pos] == 5) { | ||
566 | g->guess[pos] = 4; return TRUE; | ||
567 | } | ||
568 | } | ||
569 | |||
570 | if (g->guess[pos] == 2) { | ||
571 | if (g->possible[pos] == 2) { | ||
572 | return next_list(g,pos-1); | ||
573 | } | ||
574 | if (g->possible[pos] == 3) { | ||
575 | g->guess[pos] = 1; return next_list(g,pos-1); | ||
576 | } | ||
577 | if (g->possible[pos] == 6 || g->possible[pos] == 7) { | ||
578 | g->guess[pos] = 4; return TRUE; | ||
579 | } | ||
580 | } | ||
581 | |||
582 | if (g->guess[pos] == 4) { | ||
583 | if (g->possible[pos] == 5 || g->possible[pos] == 7) { | ||
584 | g->guess[pos] = 1; return next_list(g,pos-1); | ||
585 | } | ||
586 | if (g->possible[pos] == 6) { | ||
587 | g->guess[pos] = 2; return next_list(g,pos-1); | ||
588 | } | ||
589 | if (g->possible[pos] == 4) { | ||
590 | return next_list(g,pos-1); | ||
591 | } | ||
592 | } | ||
593 | return FALSE; | ||
594 | } | ||
595 | |||
596 | void get_unique(game_state *state, int counter, random_state *rs) { | ||
597 | |||
598 | int p,i,c,pathlimit,count_uniques; | ||
599 | struct guess path_guess; | ||
600 | int *view_count; | ||
601 | |||
602 | struct entry { | ||
603 | struct entry *link; | ||
604 | int *guess; | ||
605 | int start_view; | ||
606 | int end_view; | ||
607 | }; | ||
608 | |||
609 | struct { | ||
610 | struct entry *head; | ||
611 | struct entry *node; | ||
612 | } views, single_views, test_views; | ||
613 | |||
614 | struct entry test_entry; | ||
615 | |||
616 | path_guess.length = state->common->paths[counter].num_monsters; | ||
617 | path_guess.guess = snewn(path_guess.length,int); | ||
618 | path_guess.possible = snewn(path_guess.length,int); | ||
619 | for (i=0;i<path_guess.length;i++) | ||
620 | path_guess.guess[i] = path_guess.possible[i] = 0; | ||
621 | |||
622 | for (p=0;p<path_guess.length;p++) { | ||
623 | path_guess.possible[p] = | ||
624 | state->guess[state->common->paths[counter].mapping[p]]; | ||
625 | switch (path_guess.possible[p]) { | ||
626 | case 1: path_guess.guess[p] = 1; break; | ||
627 | case 2: path_guess.guess[p] = 2; break; | ||
628 | case 3: path_guess.guess[p] = 1; break; | ||
629 | case 4: path_guess.guess[p] = 4; break; | ||
630 | case 5: path_guess.guess[p] = 1; break; | ||
631 | case 6: path_guess.guess[p] = 2; break; | ||
632 | case 7: path_guess.guess[p] = 1; break; | ||
633 | } | ||
634 | } | ||
635 | |||
636 | views.head = NULL; | ||
637 | views.node = NULL; | ||
638 | |||
639 | pathlimit = state->common->paths[counter].length + 1; | ||
640 | view_count = snewn(pathlimit*pathlimit, int); | ||
641 | for (i = 0; i < pathlimit*pathlimit; i++) | ||
642 | view_count[i] = 0; | ||
643 | |||
644 | do { | ||
645 | int mirror, start_view, end_view; | ||
646 | |||
647 | mirror = FALSE; | ||
648 | start_view = 0; | ||
649 | for (p=0;p<state->common->paths[counter].length;p++) { | ||
650 | if (state->common->paths[counter].p[p] == -1) mirror = TRUE; | ||
651 | else { | ||
652 | for (i=0;i<path_guess.length;i++) { | ||
653 | if (state->common->paths[counter].p[p] == | ||
654 | state->common->paths[counter].mapping[i]) { | ||
655 | if (path_guess.guess[i] == 1 && mirror == TRUE) | ||
656 | start_view++; | ||
657 | if (path_guess.guess[i] == 2 && mirror == FALSE) | ||
658 | start_view++; | ||
659 | if (path_guess.guess[i] == 4) | ||
660 | start_view++; | ||
661 | break; | ||
662 | } | ||
663 | } | ||
664 | } | ||
665 | } | ||
666 | mirror = FALSE; | ||
667 | end_view = 0; | ||
668 | for (p=state->common->paths[counter].length-1;p>=0;p--) { | ||
669 | if (state->common->paths[counter].p[p] == -1) mirror = TRUE; | ||
670 | else { | ||
671 | for (i=0;i<path_guess.length;i++) { | ||
672 | if (state->common->paths[counter].p[p] == | ||
673 | state->common->paths[counter].mapping[i]) { | ||
674 | if (path_guess.guess[i] == 1 && mirror == TRUE) | ||
675 | end_view++; | ||
676 | if (path_guess.guess[i] == 2 && mirror == FALSE) | ||
677 | end_view++; | ||
678 | if (path_guess.guess[i] == 4) | ||
679 | end_view++; | ||
680 | break; | ||
681 | } | ||
682 | } | ||
683 | } | ||
684 | } | ||
685 | |||
686 | assert(start_view >= 0 && start_view < pathlimit); | ||
687 | assert(end_view >= 0 && end_view < pathlimit); | ||
688 | i = start_view * pathlimit + end_view; | ||
689 | view_count[i]++; | ||
690 | if (view_count[i] == 1) { | ||
691 | views.node = snewn(1,struct entry); | ||
692 | views.node->link = views.head; | ||
693 | views.node->guess = snewn(path_guess.length,int); | ||
694 | views.head = views.node; | ||
695 | views.node->start_view = start_view; | ||
696 | views.node->end_view = end_view; | ||
697 | memcpy(views.node->guess, path_guess.guess, | ||
698 | path_guess.length*sizeof(int)); | ||
699 | } | ||
700 | } while (next_list(&path_guess, path_guess.length-1)); | ||
701 | |||
702 | /* extract single entries from view list */ | ||
703 | |||
704 | test_views.head = views.head; | ||
705 | test_views.node = views.node; | ||
706 | |||
707 | test_entry.guess = snewn(path_guess.length,int); | ||
708 | |||
709 | single_views.head = NULL; | ||
710 | single_views.node = NULL; | ||
711 | |||
712 | count_uniques = 0; | ||
713 | while (test_views.head != NULL) { | ||
714 | test_views.node = test_views.head; | ||
715 | test_views.head = test_views.head->link; | ||
716 | i = test_views.node->start_view * pathlimit + test_views.node->end_view; | ||
717 | if (view_count[i] == 1) { | ||
718 | single_views.node = snewn(1,struct entry); | ||
719 | single_views.node->link = single_views.head; | ||
720 | single_views.node->guess = snewn(path_guess.length,int); | ||
721 | single_views.head = single_views.node; | ||
722 | single_views.node->start_view = test_views.node->start_view; | ||
723 | single_views.node->end_view = test_views.node->end_view; | ||
724 | memcpy(single_views.node->guess, test_views.node->guess, | ||
725 | path_guess.length*sizeof(int)); | ||
726 | count_uniques++; | ||
727 | } | ||
728 | } | ||
729 | |||
730 | sfree(view_count); | ||
731 | |||
732 | if (count_uniques > 0) { | ||
733 | test_entry.start_view = 0; | ||
734 | test_entry.end_view = 0; | ||
735 | /* Choose one unique guess per random */ | ||
736 | /* While we are busy with looping through single_views, we | ||
737 | * conveniently free the linked list single_view */ | ||
738 | c = random_upto(rs,count_uniques); | ||
739 | while(single_views.head != NULL) { | ||
740 | single_views.node = single_views.head; | ||
741 | single_views.head = single_views.head->link; | ||
742 | if (c-- == 0) { | ||
743 | memcpy(test_entry.guess, single_views.node->guess, | ||
744 | path_guess.length*sizeof(int)); | ||
745 | test_entry.start_view = single_views.node->start_view; | ||
746 | test_entry.end_view = single_views.node->end_view; | ||
747 | } | ||
748 | sfree(single_views.node->guess); | ||
749 | sfree(single_views.node); | ||
750 | } | ||
751 | |||
752 | /* Modify state_guess according to path_guess.mapping */ | ||
753 | for (i=0;i<path_guess.length;i++) | ||
754 | state->guess[state->common->paths[counter].mapping[i]] = | ||
755 | test_entry.guess[i]; | ||
756 | } | ||
757 | |||
758 | sfree(test_entry.guess); | ||
759 | |||
760 | while (views.head != NULL) { | ||
761 | views.node = views.head; | ||
762 | views.head = views.head->link; | ||
763 | sfree(views.node->guess); | ||
764 | sfree(views.node); | ||
765 | } | ||
766 | |||
767 | sfree(path_guess.possible); | ||
768 | sfree(path_guess.guess); | ||
769 | |||
770 | return; | ||
771 | } | ||
772 | |||
773 | int count_monsters(game_state *state, | ||
774 | int *cGhost, int *cVampire, int *cZombie) { | ||
775 | int cNone; | ||
776 | int i; | ||
777 | |||
778 | *cGhost = *cVampire = *cZombie = cNone = 0; | ||
779 | |||
780 | for (i=0;i<state->common->num_total;i++) { | ||
781 | if (state->guess[i] == 1) (*cGhost)++; | ||
782 | else if (state->guess[i] == 2) (*cVampire)++; | ||
783 | else if (state->guess[i] == 4) (*cZombie)++; | ||
784 | else cNone++; | ||
785 | } | ||
786 | |||
787 | return cNone; | ||
788 | } | ||
789 | |||
790 | int check_numbers(game_state *state, int *guess) { | ||
791 | int valid; | ||
792 | int i; | ||
793 | int count_ghosts, count_vampires, count_zombies; | ||
794 | |||
795 | count_ghosts = count_vampires = count_zombies = 0; | ||
796 | for (i=0;i<state->common->num_total;i++) { | ||
797 | if (guess[i] == 1) count_ghosts++; | ||
798 | if (guess[i] == 2) count_vampires++; | ||
799 | if (guess[i] == 4) count_zombies++; | ||
800 | } | ||
801 | |||
802 | valid = TRUE; | ||
803 | |||
804 | if (count_ghosts > state->common->num_ghosts) valid = FALSE; | ||
805 | if (count_vampires > state->common->num_vampires) valid = FALSE; | ||
806 | if (count_zombies > state->common->num_zombies) valid = FALSE; | ||
807 | |||
808 | return valid; | ||
809 | } | ||
810 | |||
811 | int check_solution(int *g, struct path path) { | ||
812 | int i; | ||
813 | int mirror; | ||
814 | int count; | ||
815 | |||
816 | count = 0; | ||
817 | mirror = FALSE; | ||
818 | for (i=0;i<path.length;i++) { | ||
819 | if (path.p[i] == -1) mirror = TRUE; | ||
820 | else { | ||
821 | if (g[path.p[i]] == 1 && mirror) count++; | ||
822 | else if (g[path.p[i]] == 2 && !mirror) count++; | ||
823 | else if (g[path.p[i]] == 4) count++; | ||
824 | } | ||
825 | } | ||
826 | if (count != path.sightings_start) return FALSE; | ||
827 | |||
828 | count = 0; | ||
829 | mirror = FALSE; | ||
830 | for (i=path.length-1;i>=0;i--) { | ||
831 | if (path.p[i] == -1) mirror = TRUE; | ||
832 | else { | ||
833 | if (g[path.p[i]] == 1 && mirror) count++; | ||
834 | else if (g[path.p[i]] == 2 && !mirror) count++; | ||
835 | else if (g[path.p[i]] == 4) count++; | ||
836 | } | ||
837 | } | ||
838 | if (count != path.sightings_end) return FALSE; | ||
839 | |||
840 | return TRUE; | ||
841 | } | ||
842 | |||
843 | int solve_iterative(game_state *state, struct path *paths) { | ||
844 | int solved; | ||
845 | int p,i,j,count; | ||
846 | |||
847 | int *guess; | ||
848 | int *possible; | ||
849 | |||
850 | struct guess loop; | ||
851 | |||
852 | solved = TRUE; | ||
853 | loop.length = state->common->num_total; | ||
854 | guess = snewn(state->common->num_total,int); | ||
855 | possible = snewn(state->common->num_total,int); | ||
856 | |||
857 | for (i=0;i<state->common->num_total;i++) { | ||
858 | guess[i] = state->guess[i]; | ||
859 | possible[i] = 0; | ||
860 | } | ||
861 | |||
862 | for (p=0;p<state->common->num_paths;p++) { | ||
863 | if (paths[p].num_monsters > 0) { | ||
864 | loop.length = paths[p].num_monsters; | ||
865 | loop.guess = snewn(paths[p].num_monsters,int); | ||
866 | loop.possible = snewn(paths[p].num_monsters,int); | ||
867 | |||
868 | for (i=0;i<paths[p].num_monsters;i++) { | ||
869 | switch (state->guess[paths[p].mapping[i]]) { | ||
870 | case 1: loop.guess[i] = 1; break; | ||
871 | case 2: loop.guess[i] = 2; break; | ||
872 | case 3: loop.guess[i] = 1; break; | ||
873 | case 4: loop.guess[i] = 4; break; | ||
874 | case 5: loop.guess[i] = 1; break; | ||
875 | case 6: loop.guess[i] = 2; break; | ||
876 | case 7: loop.guess[i] = 1; break; | ||
877 | } | ||
878 | loop.possible[i] = state->guess[paths[p].mapping[i]]; | ||
879 | possible[paths[p].mapping[i]] = 0; | ||
880 | } | ||
881 | |||
882 | while(TRUE) { | ||
883 | for (i=0;i<state->common->num_total;i++) { | ||
884 | guess[i] = state->guess[i]; | ||
885 | } | ||
886 | count = 0; | ||
887 | for (i=0;i<paths[p].num_monsters;i++) | ||
888 | guess[paths[p].mapping[i]] = loop.guess[count++]; | ||
889 | if (check_numbers(state,guess) && | ||
890 | check_solution(guess,paths[p])) | ||
891 | for (j=0;j<paths[p].num_monsters;j++) | ||
892 | possible[paths[p].mapping[j]] |= loop.guess[j]; | ||
893 | if (!next_list(&loop,loop.length-1)) break; | ||
894 | } | ||
895 | for (i=0;i<paths[p].num_monsters;i++) | ||
896 | state->guess[paths[p].mapping[i]] &= | ||
897 | possible[paths[p].mapping[i]]; | ||
898 | sfree(loop.possible); | ||
899 | sfree(loop.guess); | ||
900 | } | ||
901 | } | ||
902 | |||
903 | for (i=0;i<state->common->num_total;i++) { | ||
904 | if (state->guess[i] == 3 || state->guess[i] == 5 || | ||
905 | state->guess[i] == 6 || state->guess[i] == 7) { | ||
906 | solved = FALSE; break; | ||
907 | } | ||
908 | } | ||
909 | |||
910 | sfree(possible); | ||
911 | sfree(guess); | ||
912 | |||
913 | return solved; | ||
914 | } | ||
915 | |||
916 | int solve_bruteforce(game_state *state, struct path *paths) { | ||
917 | int solved, correct; | ||
918 | int number_solutions; | ||
919 | int p,i; | ||
920 | |||
921 | struct guess loop; | ||
922 | |||
923 | loop.guess = snewn(state->common->num_total,int); | ||
924 | loop.possible = snewn(state->common->num_total,int); | ||
925 | |||
926 | for (i=0;i<state->common->num_total;i++) { | ||
927 | loop.possible[i] = state->guess[i]; | ||
928 | switch (state->guess[i]) { | ||
929 | case 1: loop.guess[i] = 1; break; | ||
930 | case 2: loop.guess[i] = 2; break; | ||
931 | case 3: loop.guess[i] = 1; break; | ||
932 | case 4: loop.guess[i] = 4; break; | ||
933 | case 5: loop.guess[i] = 1; break; | ||
934 | case 6: loop.guess[i] = 2; break; | ||
935 | case 7: loop.guess[i] = 1; break; | ||
936 | } | ||
937 | } | ||
938 | |||
939 | solved = FALSE; | ||
940 | number_solutions = 0; | ||
941 | |||
942 | while (TRUE) { | ||
943 | |||
944 | correct = TRUE; | ||
945 | if (!check_numbers(state,loop.guess)) correct = FALSE; | ||
946 | else | ||
947 | for (p=0;p<state->common->num_paths;p++) | ||
948 | if (!check_solution(loop.guess,paths[p])) { | ||
949 | correct = FALSE; break; | ||
950 | } | ||
951 | if (correct) { | ||
952 | number_solutions++; | ||
953 | solved = TRUE; | ||
954 | if(number_solutions > 1) { | ||
955 | solved = FALSE; | ||
956 | break; | ||
957 | } | ||
958 | for (i=0;i<state->common->num_total;i++) | ||
959 | state->guess[i] = loop.guess[i]; | ||
960 | } | ||
961 | if (!next_list(&loop,state->common->num_total -1)) { | ||
962 | break; | ||
963 | } | ||
964 | } | ||
965 | |||
966 | sfree(loop.possible); | ||
967 | sfree(loop.guess); | ||
968 | |||
969 | return solved; | ||
970 | } | ||
971 | |||
972 | int path_cmp(const void *a, const void *b) { | ||
973 | const struct path *pa = (const struct path *)a; | ||
974 | const struct path *pb = (const struct path *)b; | ||
975 | return pa->num_monsters - pb->num_monsters; | ||
976 | } | ||
977 | |||
978 | static char *new_game_desc(const game_params *params, random_state *rs, | ||
979 | char **aux, int interactive) { | ||
980 | int i,count,c,w,h,r,p,g; | ||
981 | game_state *new; | ||
982 | |||
983 | /* Variables for puzzle generation algorithm */ | ||
984 | int filling; | ||
985 | int max_length; | ||
986 | int count_ghosts, count_vampires, count_zombies; | ||
987 | int abort; | ||
988 | float ratio; | ||
989 | |||
990 | /* Variables for solver algorithm */ | ||
991 | int solved_iterative, solved_bruteforce, contains_inconsistency, | ||
992 | count_ambiguous; | ||
993 | int iterative_depth; | ||
994 | int *old_guess; | ||
995 | |||
996 | /* Variables for game description generation */ | ||
997 | int x,y; | ||
998 | char *e; | ||
999 | char *desc; | ||
1000 | |||
1001 | i = 0; | ||
1002 | while (TRUE) { | ||
1003 | new = new_state(params); | ||
1004 | abort = FALSE; | ||
1005 | |||
1006 | /* Fill grid with random mirrors and (later to be populated) | ||
1007 | * empty monster cells */ | ||
1008 | count = 0; | ||
1009 | for (h=1;h<new->common->params.h+1;h++) | ||
1010 | for (w=1;w<new->common->params.w+1;w++) { | ||
1011 | c = random_upto(rs,5); | ||
1012 | if (c >= 2) { | ||
1013 | new->common->grid[w+h*(new->common->params.w+2)] = CELL_EMPTY; | ||
1014 | new->common->xinfo[w+h*(new->common->params.w+2)] = count++; | ||
1015 | } | ||
1016 | else if (c == 0) { | ||
1017 | new->common->grid[w+h*(new->common->params.w+2)] = | ||
1018 | CELL_MIRROR_L; | ||
1019 | new->common->xinfo[w+h*(new->common->params.w+2)] = -1; | ||
1020 | } | ||
1021 | else { | ||
1022 | new->common->grid[w+h*(new->common->params.w+2)] = | ||
1023 | CELL_MIRROR_R; | ||
1024 | new->common->xinfo[w+h*(new->common->params.w+2)] = -1; | ||
1025 | } | ||
1026 | } | ||
1027 | new->common->num_total = count; /* Total number of monsters in maze */ | ||
1028 | |||
1029 | /* Puzzle is boring if it has too few monster cells. Discard | ||
1030 | * grid, make new grid */ | ||
1031 | if (new->common->num_total <= 4) { | ||
1032 | free_game(new); | ||
1033 | continue; | ||
1034 | } | ||
1035 | |||
1036 | /* Monsters / Mirrors ratio should be balanced */ | ||
1037 | ratio = (float)new->common->num_total / | ||
1038 | (float)(new->common->params.w * new->common->params.h); | ||
1039 | if (ratio < 0.48 || ratio > 0.78) { | ||
1040 | free_game(new); | ||
1041 | continue; | ||
1042 | } | ||
1043 | |||
1044 | /* Assign clue identifiers */ | ||
1045 | for (r=0;r<2*(new->common->params.w+new->common->params.h);r++) { | ||
1046 | int x,y,gridno; | ||
1047 | gridno = range2grid(r,new->common->params.w,new->common->params.h, | ||
1048 | &x,&y); | ||
1049 | new->common->grid[x+y*(new->common->params.w +2)] = gridno; | ||
1050 | new->common->xinfo[x+y*(new->common->params.w +2)] = 0; | ||
1051 | } | ||
1052 | /* The four corners don't matter at all for the game. Set them | ||
1053 | * all to zero, just to have a nice data structure */ | ||
1054 | new->common->grid[0] = 0; | ||
1055 | new->common->xinfo[0] = 0; | ||
1056 | new->common->grid[new->common->params.w+1] = 0; | ||
1057 | new->common->xinfo[new->common->params.w+1] = 0; | ||
1058 | new->common->grid[new->common->params.w+1 + (new->common->params.h+1)*(new->common->params.w+2)] = 0; | ||
1059 | new->common->xinfo[new->common->params.w+1 + (new->common->params.h+1)*(new->common->params.w+2)] = 0; | ||
1060 | new->common->grid[(new->common->params.h+1)*(new->common->params.w+2)] = 0; | ||
1061 | new->common->xinfo[(new->common->params.h+1)*(new->common->params.w+2)] = 0; | ||
1062 | |||
1063 | /* Initialize solution vector */ | ||
1064 | new->guess = snewn(new->common->num_total,int); | ||
1065 | for (g=0;g<new->common->num_total;g++) new->guess[g] = 7; | ||
1066 | |||
1067 | /* Initialize fixed flag from common. Not needed for the | ||
1068 | * puzzle generator; initialize it for having clean code */ | ||
1069 | new->common->fixed = snewn(new->common->num_total,int); | ||
1070 | for (g=0;g<new->common->num_total;g++) | ||
1071 | new->common->fixed[g] = FALSE; | ||
1072 | |||
1073 | /* paths generation */ | ||
1074 | make_paths(new); | ||
1075 | |||
1076 | /* Grid is invalid if max. path length > threshold. Discard | ||
1077 | * grid, make new one */ | ||
1078 | switch (new->common->params.diff) { | ||
1079 | case DIFF_EASY: max_length = min(new->common->params.w,new->common->params.h) + 1; break; | ||
1080 | case DIFF_NORMAL: max_length = (max(new->common->params.w,new->common->params.h) * 3) / 2; break; | ||
1081 | case DIFF_TRICKY: max_length = 9; break; | ||
1082 | default: max_length = 9; break; | ||
1083 | } | ||
1084 | |||
1085 | for (p=0;p<new->common->num_paths;p++) { | ||
1086 | if (new->common->paths[p].num_monsters > max_length) { | ||
1087 | abort = TRUE; | ||
1088 | } | ||
1089 | } | ||
1090 | if (abort) { | ||
1091 | free_game(new); | ||
1092 | continue; | ||
1093 | } | ||
1094 | |||
1095 | qsort(new->common->paths, new->common->num_paths, | ||
1096 | sizeof(struct path), path_cmp); | ||
1097 | |||
1098 | /* Grid monster initialization */ | ||
1099 | /* For easy puzzles, we try to fill nearly the whole grid | ||
1100 | with unique solution paths (up to 2) For more difficult | ||
1101 | puzzles, we fill only roughly half the grid, and choose | ||
1102 | random monsters for the rest For hard puzzles, we fill | ||
1103 | even less paths with unique solutions */ | ||
1104 | |||
1105 | switch (new->common->params.diff) { | ||
1106 | case DIFF_EASY: filling = 2; break; | ||
1107 | case DIFF_NORMAL: filling = min( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break; | ||
1108 | case DIFF_TRICKY: filling = max( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break; | ||
1109 | default: filling = 0; break; | ||
1110 | } | ||
1111 | |||
1112 | count = 0; | ||
1113 | while ( (count_monsters(new, &count_ghosts, &count_vampires, | ||
1114 | &count_zombies)) > filling) { | ||
1115 | if ((count) >= new->common->num_paths) break; | ||
1116 | if (new->common->paths[count].num_monsters == 0) { | ||
1117 | count++; | ||
1118 | continue; | ||
1119 | } | ||
1120 | get_unique(new,count,rs); | ||
1121 | count++; | ||
1122 | } | ||
1123 | |||
1124 | /* Fill any remaining ambiguous entries with random monsters */ | ||
1125 | for(g=0;g<new->common->num_total;g++) { | ||
1126 | if (new->guess[g] == 7) { | ||
1127 | r = random_upto(rs,3); | ||
1128 | new->guess[g] = (r == 0) ? 1 : ( (r == 1) ? 2 : 4 ); | ||
1129 | } | ||
1130 | } | ||
1131 | |||
1132 | /* Determine all hints */ | ||
1133 | count_monsters(new, &new->common->num_ghosts, | ||
1134 | &new->common->num_vampires, &new->common->num_zombies); | ||
1135 | |||
1136 | /* Puzzle is trivial if it has only one type of monster. Discard. */ | ||
1137 | if ((new->common->num_ghosts == 0 && new->common->num_vampires == 0) || | ||
1138 | (new->common->num_ghosts == 0 && new->common->num_zombies == 0) || | ||
1139 | (new->common->num_vampires == 0 && new->common->num_zombies == 0)) { | ||
1140 | free_game(new); | ||
1141 | continue; | ||
1142 | } | ||
1143 | |||
1144 | /* Discard puzzle if difficulty Tricky, and it has only 1 | ||
1145 | * member of any monster type */ | ||
1146 | if (new->common->params.diff == DIFF_TRICKY && | ||
1147 | (new->common->num_ghosts <= 1 || | ||
1148 | new->common->num_vampires <= 1 || new->common->num_zombies <= 1)) { | ||
1149 | free_game(new); | ||
1150 | continue; | ||
1151 | } | ||
1152 | |||
1153 | for (w=1;w<new->common->params.w+1;w++) | ||
1154 | for (h=1;h<new->common->params.h+1;h++) { | ||
1155 | c = new->common->xinfo[w+h*(new->common->params.w+2)]; | ||
1156 | if (c >= 0) { | ||
1157 | if (new->guess[c] == 1) new->common->grid[w+h*(new->common->params.w+2)] = CELL_GHOST; | ||
1158 | if (new->guess[c] == 2) new->common->grid[w+h*(new->common->params.w+2)] = CELL_VAMPIRE; | ||
1159 | if (new->guess[c] == 4) new->common->grid[w+h*(new->common->params.w+2)] = CELL_ZOMBIE; | ||
1160 | } | ||
1161 | } | ||
1162 | |||
1163 | /* Prepare path information needed by the solver (containing all hints) */ | ||
1164 | for (p=0;p<new->common->num_paths;p++) { | ||
1165 | int mirror,x,y; | ||
1166 | |||
1167 | new->common->paths[p].sightings_start = 0; | ||
1168 | new->common->paths[p].sightings_end = 0; | ||
1169 | |||
1170 | mirror = FALSE; | ||
1171 | for (g=0;g<new->common->paths[p].length;g++) { | ||
1172 | |||
1173 | if (new->common->paths[p].p[g] == -1) mirror = TRUE; | ||
1174 | else { | ||
1175 | if (new->guess[new->common->paths[p].p[g]] == 1 && mirror == TRUE) (new->common->paths[p].sightings_start)++; | ||
1176 | else if (new->guess[new->common->paths[p].p[g]] == 2 && mirror == FALSE) (new->common->paths[p].sightings_start)++; | ||
1177 | else if (new->guess[new->common->paths[p].p[g]] == 4) (new->common->paths[p].sightings_start)++; | ||
1178 | } | ||
1179 | } | ||
1180 | |||
1181 | mirror = FALSE; | ||
1182 | for (g=new->common->paths[p].length-1;g>=0;g--) { | ||
1183 | if (new->common->paths[p].p[g] == -1) mirror = TRUE; | ||
1184 | else { | ||
1185 | if (new->guess[new->common->paths[p].p[g]] == 1 && mirror == TRUE) (new->common->paths[p].sightings_end)++; | ||
1186 | else if (new->guess[new->common->paths[p].p[g]] == 2 && mirror == FALSE) (new->common->paths[p].sightings_end)++; | ||
1187 | else if (new->guess[new->common->paths[p].p[g]] == 4) (new->common->paths[p].sightings_end)++; | ||
1188 | } | ||
1189 | } | ||
1190 | |||
1191 | range2grid(new->common->paths[p].grid_start, | ||
1192 | new->common->params.w,new->common->params.h,&x,&y); | ||
1193 | new->common->grid[x+y*(new->common->params.w +2)] = | ||
1194 | new->common->paths[p].sightings_start; | ||
1195 | range2grid(new->common->paths[p].grid_end, | ||
1196 | new->common->params.w,new->common->params.h,&x,&y); | ||
1197 | new->common->grid[x+y*(new->common->params.w +2)] = | ||
1198 | new->common->paths[p].sightings_end; | ||
1199 | } | ||
1200 | |||
1201 | /* Try to solve the puzzle with the iterative solver */ | ||
1202 | old_guess = snewn(new->common->num_total,int); | ||
1203 | for (p=0;p<new->common->num_total;p++) { | ||
1204 | new->guess[p] = 7; | ||
1205 | old_guess[p] = 7; | ||
1206 | } | ||
1207 | iterative_depth = 0; | ||
1208 | solved_iterative = FALSE; | ||
1209 | contains_inconsistency = FALSE; | ||
1210 | count_ambiguous = 0; | ||
1211 | |||
1212 | while (TRUE) { | ||
1213 | int no_change; | ||
1214 | no_change = TRUE; | ||
1215 | solved_iterative = solve_iterative(new,new->common->paths); | ||
1216 | iterative_depth++; | ||
1217 | for (p=0;p<new->common->num_total;p++) { | ||
1218 | if (new->guess[p] != old_guess[p]) no_change = FALSE; | ||
1219 | old_guess[p] = new->guess[p]; | ||
1220 | if (new->guess[p] == 0) contains_inconsistency = TRUE; | ||
1221 | } | ||
1222 | if (solved_iterative || no_change) break; | ||
1223 | } | ||
1224 | |||
1225 | /* If necessary, try to solve the puzzle with the brute-force solver */ | ||
1226 | solved_bruteforce = FALSE; | ||
1227 | if (new->common->params.diff != DIFF_EASY && | ||
1228 | !solved_iterative && !contains_inconsistency) { | ||
1229 | for (p=0;p<new->common->num_total;p++) | ||
1230 | if (new->guess[p] != 1 && new->guess[p] != 2 && | ||
1231 | new->guess[p] != 4) count_ambiguous++; | ||
1232 | |||
1233 | solved_bruteforce = solve_bruteforce(new, new->common->paths); | ||
1234 | } | ||
1235 | |||
1236 | /* Determine puzzle difficulty level */ | ||
1237 | if (new->common->params.diff == DIFF_EASY && solved_iterative && | ||
1238 | iterative_depth <= 3 && !contains_inconsistency) { | ||
1239 | /* printf("Puzzle level: EASY Level %d Ratio %f Ambiguous %d (Found after %i tries)\n",iterative_depth, ratio, count_ambiguous, i); */ | ||
1240 | break; | ||
1241 | } | ||
1242 | |||
1243 | if (new->common->params.diff == DIFF_NORMAL && | ||
1244 | ((solved_iterative && iterative_depth > 3) || | ||
1245 | (solved_bruteforce && count_ambiguous < 4)) && | ||
1246 | !contains_inconsistency) { | ||
1247 | /* printf("Puzzle level: NORMAL Level %d Ratio %f Ambiguous %d (Found after %d tries)\n", iterative_depth, ratio, count_ambiguous, i); */ | ||
1248 | break; | ||
1249 | } | ||
1250 | if (new->common->params.diff == DIFF_TRICKY && | ||
1251 | solved_bruteforce && iterative_depth > 0 && | ||
1252 | count_ambiguous >= 4 && !contains_inconsistency) { | ||
1253 | /* printf("Puzzle level: TRICKY Level %d Ratio %f Ambiguous %d (Found after %d tries)\n", iterative_depth, ratio, count_ambiguous, i); */ | ||
1254 | break; | ||
1255 | } | ||
1256 | |||
1257 | /* If puzzle is not solvable or does not satisfy the desired | ||
1258 | * difficulty level, free memory and start from scratch */ | ||
1259 | sfree(old_guess); | ||
1260 | free_game(new); | ||
1261 | i++; | ||
1262 | } | ||
1263 | |||
1264 | /* We have a valid puzzle! */ | ||
1265 | |||
1266 | desc = snewn(10 + new->common->wh + | ||
1267 | 6*(new->common->params.w + new->common->params.h), char); | ||
1268 | e = desc; | ||
1269 | |||
1270 | /* Encode monster counts */ | ||
1271 | e += sprintf(e, "%d,", new->common->num_ghosts); | ||
1272 | e += sprintf(e, "%d,", new->common->num_vampires); | ||
1273 | e += sprintf(e, "%d,", new->common->num_zombies); | ||
1274 | |||
1275 | /* Encode grid */ | ||
1276 | count = 0; | ||
1277 | for (y=1;y<new->common->params.h+1;y++) | ||
1278 | for (x=1;x<new->common->params.w+1;x++) { | ||
1279 | c = new->common->grid[x+y*(new->common->params.w+2)]; | ||
1280 | if (count > 25) { | ||
1281 | *e++ = 'z'; | ||
1282 | count -= 26; | ||
1283 | } | ||
1284 | if (c != CELL_MIRROR_L && c != CELL_MIRROR_R) { | ||
1285 | count++; | ||
1286 | } | ||
1287 | else if (c == CELL_MIRROR_L) { | ||
1288 | if (count > 0) *e++ = (count-1 + 'a'); | ||
1289 | *e++ = 'L'; | ||
1290 | count = 0; | ||
1291 | } | ||
1292 | else { | ||
1293 | if (count > 0) *e++ = (count-1 + 'a'); | ||
1294 | *e++ = 'R'; | ||
1295 | count = 0; | ||
1296 | } | ||
1297 | } | ||
1298 | if (count > 0) *e++ = (count-1 + 'a'); | ||
1299 | |||
1300 | /* Encode hints */ | ||
1301 | for (p=0;p<2*(new->common->params.w + new->common->params.h);p++) { | ||
1302 | range2grid(p,new->common->params.w,new->common->params.h,&x,&y); | ||
1303 | e += sprintf(e, ",%d", new->common->grid[x+y*(new->common->params.w+2)]); | ||
1304 | } | ||
1305 | |||
1306 | *e++ = '\0'; | ||
1307 | desc = sresize(desc, e - desc, char); | ||
1308 | |||
1309 | sfree(old_guess); | ||
1310 | free_game(new); | ||
1311 | |||
1312 | return desc; | ||
1313 | } | ||
1314 | |||
1315 | void num2grid(int num, int width, int height, int *x, int *y) { | ||
1316 | *x = 1+(num%width); | ||
1317 | *y = 1+(num/width); | ||
1318 | return; | ||
1319 | } | ||
1320 | |||
1321 | static game_state *new_game(midend *me, const game_params *params, | ||
1322 | const char *desc) | ||
1323 | { | ||
1324 | int i; | ||
1325 | int n; | ||
1326 | int count; | ||
1327 | |||
1328 | game_state *state = new_state(params); | ||
1329 | |||
1330 | state->common->num_ghosts = atoi(desc); | ||
1331 | while (*desc && isdigit((unsigned char)*desc)) desc++; | ||
1332 | desc++; | ||
1333 | state->common->num_vampires = atoi(desc); | ||
1334 | while (*desc && isdigit((unsigned char)*desc)) desc++; | ||
1335 | desc++; | ||
1336 | state->common->num_zombies = atoi(desc); | ||
1337 | while (*desc && isdigit((unsigned char)*desc)) desc++; | ||
1338 | desc++; | ||
1339 | |||
1340 | state->common->num_total = state->common->num_ghosts + state->common->num_vampires + state->common->num_zombies; | ||
1341 | |||
1342 | state->guess = snewn(state->common->num_total,int); | ||
1343 | state->pencils = snewn(state->common->num_total,unsigned char); | ||
1344 | state->common->fixed = snewn(state->common->num_total,int); | ||
1345 | for (i=0;i<state->common->num_total;i++) { | ||
1346 | state->guess[i] = 7; | ||
1347 | state->pencils[i] = 0; | ||
1348 | state->common->fixed[i] = FALSE; | ||
1349 | } | ||
1350 | for (i=0;i<state->common->wh;i++) | ||
1351 | state->cell_errors[i] = FALSE; | ||
1352 | for (i=0;i<2*state->common->num_paths;i++) | ||
1353 | state->hint_errors[i] = FALSE; | ||
1354 | for (i=0;i<3;i++) | ||
1355 | state->count_errors[i] = FALSE; | ||
1356 | |||
1357 | count = 0; | ||
1358 | n = 0; | ||
1359 | while (*desc != ',') { | ||
1360 | int c; | ||
1361 | int x,y; | ||
1362 | |||
1363 | if (*desc == 'L') { | ||
1364 | num2grid(n,state->common->params.w,state->common->params.h,&x,&y); | ||
1365 | state->common->grid[x+y*(state->common->params.w +2)] = CELL_MIRROR_L; | ||
1366 | state->common->xinfo[x+y*(state->common->params.w+2)] = -1; | ||
1367 | n++; | ||
1368 | } | ||
1369 | else if (*desc == 'R') { | ||
1370 | num2grid(n,state->common->params.w,state->common->params.h,&x,&y); | ||
1371 | state->common->grid[x+y*(state->common->params.w +2)] = CELL_MIRROR_R; | ||
1372 | state->common->xinfo[x+y*(state->common->params.w+2)] = -1; | ||
1373 | n++; | ||
1374 | } | ||
1375 | else if (*desc == 'G') { | ||
1376 | num2grid(n,state->common->params.w,state->common->params.h,&x,&y); | ||
1377 | state->common->grid[x+y*(state->common->params.w +2)] = CELL_GHOST; | ||
1378 | state->common->xinfo[x+y*(state->common->params.w+2)] = count; | ||
1379 | state->guess[count] = 1; | ||
1380 | state->common->fixed[count++] = TRUE; | ||
1381 | n++; | ||
1382 | } | ||
1383 | else if (*desc == 'V') { | ||
1384 | num2grid(n,state->common->params.w,state->common->params.h,&x,&y); | ||
1385 | state->common->grid[x+y*(state->common->params.w +2)] = CELL_VAMPIRE; | ||
1386 | state->common->xinfo[x+y*(state->common->params.w+2)] = count; | ||
1387 | state->guess[count] = 2; | ||
1388 | state->common->fixed[count++] = TRUE; | ||
1389 | n++; | ||
1390 | } | ||
1391 | else if (*desc == 'Z') { | ||
1392 | num2grid(n,state->common->params.w,state->common->params.h,&x,&y); | ||
1393 | state->common->grid[x+y*(state->common->params.w +2)] = CELL_ZOMBIE; | ||
1394 | state->common->xinfo[x+y*(state->common->params.w+2)] = count; | ||
1395 | state->guess[count] = 4; | ||
1396 | state->common->fixed[count++] = TRUE; | ||
1397 | n++; | ||
1398 | } | ||
1399 | else { | ||
1400 | c = *desc - ('a' -1); | ||
1401 | while (c-- > 0) { | ||
1402 | num2grid(n,state->common->params.w,state->common->params.h,&x,&y); | ||
1403 | state->common->grid[x+y*(state->common->params.w +2)] = CELL_EMPTY; | ||
1404 | state->common->xinfo[x+y*(state->common->params.w+2)] = count; | ||
1405 | state->guess[count] = 7; | ||
1406 | state->common->fixed[count++] = FALSE; | ||
1407 | n++; | ||
1408 | } | ||
1409 | } | ||
1410 | desc++; | ||
1411 | } | ||
1412 | desc++; | ||
1413 | |||
1414 | for (i=0;i<2*(state->common->params.w + state->common->params.h);i++) { | ||
1415 | int x,y; | ||
1416 | int sights; | ||
1417 | |||
1418 | sights = atoi(desc); | ||
1419 | while (*desc && isdigit((unsigned char)*desc)) desc++; | ||
1420 | desc++; | ||
1421 | |||
1422 | |||
1423 | range2grid(i,state->common->params.w,state->common->params.h,&x,&y); | ||
1424 | state->common->grid[x+y*(state->common->params.w +2)] = sights; | ||
1425 | state->common->xinfo[x+y*(state->common->params.w +2)] = -2; | ||
1426 | } | ||
1427 | |||
1428 | state->common->grid[0] = 0; | ||
1429 | state->common->xinfo[0] = -2; | ||
1430 | state->common->grid[state->common->params.w+1] = 0; | ||
1431 | state->common->xinfo[state->common->params.w+1] = -2; | ||
1432 | state->common->grid[state->common->params.w+1 + (state->common->params.h+1)*(state->common->params.w+2)] = 0; | ||
1433 | state->common->xinfo[state->common->params.w+1 + (state->common->params.h+1)*(state->common->params.w+2)] = -2; | ||
1434 | state->common->grid[(state->common->params.h+1)*(state->common->params.w+2)] = 0; | ||
1435 | state->common->xinfo[(state->common->params.h+1)*(state->common->params.w+2)] = -2; | ||
1436 | |||
1437 | make_paths(state); | ||
1438 | qsort(state->common->paths, state->common->num_paths, sizeof(struct path), path_cmp); | ||
1439 | |||
1440 | return state; | ||
1441 | } | ||
1442 | |||
1443 | static char *validate_desc(const game_params *params, const char *desc) | ||
1444 | { | ||
1445 | int i; | ||
1446 | int w = params->w, h = params->h; | ||
1447 | int wh = w*h; | ||
1448 | int area; | ||
1449 | int monsters; | ||
1450 | int monster_count; | ||
1451 | const char *desc_s = desc; | ||
1452 | |||
1453 | for (i=0;i<3;i++) { | ||
1454 | if (!*desc) return "Faulty game description"; | ||
1455 | while (*desc && isdigit((unsigned char)*desc)) { desc++; } | ||
1456 | if (*desc != ',') return "Invalid character in number list"; | ||
1457 | desc++; | ||
1458 | } | ||
1459 | desc = desc_s; | ||
1460 | |||
1461 | area = monsters = monster_count = 0; | ||
1462 | for (i=0;i<3;i++) { | ||
1463 | monster_count += atoi(desc); | ||
1464 | while (*desc && isdigit((unsigned char)*desc)) desc++; | ||
1465 | desc++; | ||
1466 | } | ||
1467 | while (*desc && *desc != ',') { | ||
1468 | if (*desc >= 'a' && *desc <= 'z') { | ||
1469 | area += *desc - 'a' +1; monsters += *desc - 'a' +1; | ||
1470 | } else if (*desc == 'G' || *desc == 'V' || *desc == 'Z') { | ||
1471 | area++; monsters++; | ||
1472 | } else if (*desc == 'L' || *desc == 'R') { | ||
1473 | area++; | ||
1474 | } else | ||
1475 | return "Invalid character in grid specification"; | ||
1476 | desc++; | ||
1477 | } | ||
1478 | if (area < wh) return "Not enough data to fill grid"; | ||
1479 | else if (area > wh) return "Too much data to fill grid"; | ||
1480 | if (monsters != monster_count) | ||
1481 | return "Monster numbers do not match grid spaces"; | ||
1482 | |||
1483 | for (i = 0; i < 2*(w+h); i++) { | ||
1484 | if (!*desc) return "Not enough numbers given after grid specification"; | ||
1485 | else if (*desc != ',') return "Invalid character in number list"; | ||
1486 | desc++; | ||
1487 | while (*desc && isdigit((unsigned char)*desc)) { desc++; } | ||
1488 | } | ||
1489 | |||
1490 | if (*desc) return "Unexpected additional data at end of game description"; | ||
1491 | |||
1492 | return NULL; | ||
1493 | } | ||
1494 | |||
1495 | static char *solve_game(const game_state *state_start, const game_state *currstate, | ||
1496 | const char *aux, char **error) | ||
1497 | { | ||
1498 | int p; | ||
1499 | int *old_guess; | ||
1500 | int iterative_depth; | ||
1501 | int solved_iterative, solved_bruteforce, contains_inconsistency, | ||
1502 | count_ambiguous; | ||
1503 | |||
1504 | int i; | ||
1505 | char *move, *c; | ||
1506 | |||
1507 | game_state *solve_state = dup_game(currstate); | ||
1508 | |||
1509 | old_guess = snewn(solve_state->common->num_total,int); | ||
1510 | for (p=0;p<solve_state->common->num_total;p++) { | ||
1511 | if (solve_state->common->fixed[p]) { | ||
1512 | old_guess[p] = solve_state->guess[p] = state_start->guess[p]; | ||
1513 | } | ||
1514 | else { | ||
1515 | old_guess[p] = solve_state->guess[p] = 7; | ||
1516 | } | ||
1517 | } | ||
1518 | iterative_depth = 0; | ||
1519 | solved_iterative = FALSE; | ||
1520 | contains_inconsistency = FALSE; | ||
1521 | count_ambiguous = 0; | ||
1522 | |||
1523 | /* Try to solve the puzzle with the iterative solver */ | ||
1524 | while (TRUE) { | ||
1525 | int no_change; | ||
1526 | no_change = TRUE; | ||
1527 | solved_iterative = | ||
1528 | solve_iterative(solve_state,solve_state->common->paths); | ||
1529 | iterative_depth++; | ||
1530 | for (p=0;p<solve_state->common->num_total;p++) { | ||
1531 | if (solve_state->guess[p] != old_guess[p]) no_change = FALSE; | ||
1532 | old_guess[p] = solve_state->guess[p]; | ||
1533 | if (solve_state->guess[p] == 0) contains_inconsistency = TRUE; | ||
1534 | } | ||
1535 | if (solved_iterative || no_change || contains_inconsistency) break; | ||
1536 | } | ||
1537 | |||
1538 | if (contains_inconsistency) { | ||
1539 | *error = "Puzzle is inconsistent"; | ||
1540 | sfree(old_guess); | ||
1541 | free_game(solve_state); | ||
1542 | return NULL; | ||
1543 | } | ||
1544 | |||
1545 | /* If necessary, try to solve the puzzle with the brute-force solver */ | ||
1546 | solved_bruteforce = FALSE; | ||
1547 | if (!solved_iterative) { | ||
1548 | for (p=0;p<solve_state->common->num_total;p++) | ||
1549 | if (solve_state->guess[p] != 1 && solve_state->guess[p] != 2 && | ||
1550 | solve_state->guess[p] != 4) count_ambiguous++; | ||
1551 | solved_bruteforce = | ||
1552 | solve_bruteforce(solve_state, solve_state->common->paths); | ||
1553 | } | ||
1554 | |||
1555 | if (!solved_iterative && !solved_bruteforce) { | ||
1556 | *error = "Puzzle is unsolvable"; | ||
1557 | sfree(old_guess); | ||
1558 | free_game(solve_state); | ||
1559 | return NULL; | ||
1560 | } | ||
1561 | |||
1562 | /* printf("Puzzle solved at level %s, iterations %d, ambiguous %d\n", (solved_bruteforce ? "TRICKY" : "NORMAL"), iterative_depth, count_ambiguous); */ | ||
1563 | |||
1564 | move = snewn(solve_state->common->num_total * 4 +2, char); | ||
1565 | c = move; | ||
1566 | *c++='S'; | ||
1567 | for (i = 0; i < solve_state->common->num_total; i++) { | ||
1568 | if (solve_state->guess[i] == 1) c += sprintf(c, ";G%d", i); | ||
1569 | if (solve_state->guess[i] == 2) c += sprintf(c, ";V%d", i); | ||
1570 | if (solve_state->guess[i] == 4) c += sprintf(c, ";Z%d", i); | ||
1571 | } | ||
1572 | *c++ = '\0'; | ||
1573 | move = sresize(move, c - move, char); | ||
1574 | |||
1575 | sfree(old_guess); | ||
1576 | free_game(solve_state); | ||
1577 | return move; | ||
1578 | } | ||
1579 | |||
1580 | static int game_can_format_as_text_now(const game_params *params) | ||
1581 | { | ||
1582 | return TRUE; | ||
1583 | } | ||
1584 | |||
1585 | static char *game_text_format(const game_state *state) | ||
1586 | { | ||
1587 | int w,h,c,r,xi,g; | ||
1588 | char *ret; | ||
1589 | char buf[120]; | ||
1590 | |||
1591 | ret = snewn(50 + 6*(state->common->params.w +2) + | ||
1592 | 6*(state->common->params.h+2) + | ||
1593 | 3*(state->common->params.w * state->common->params.h), char); | ||
1594 | |||
1595 | sprintf(ret,"G: %d V: %d Z: %d\n\n",state->common->num_ghosts, | ||
1596 | state->common->num_vampires, state->common->num_zombies); | ||
1597 | |||
1598 | for (h=0;h<state->common->params.h+2;h++) { | ||
1599 | for (w=0;w<state->common->params.w+2;w++) { | ||
1600 | c = state->common->grid[w+h*(state->common->params.w+2)]; | ||
1601 | xi = state->common->xinfo[w+h*(state->common->params.w+2)]; | ||
1602 | r = grid2range(w,h,state->common->params.w,state->common->params.h); | ||
1603 | if (r != -1) { | ||
1604 | sprintf(buf,"%2d", c); strcat(ret,buf); | ||
1605 | } else if (c == CELL_MIRROR_L) { | ||
1606 | sprintf(buf," \\"); strcat(ret,buf); | ||
1607 | } else if (c == CELL_MIRROR_R) { | ||
1608 | sprintf(buf," /"); strcat(ret,buf); | ||
1609 | } else if (xi >= 0) { | ||
1610 | g = state->guess[xi]; | ||
1611 | if (g == 1) { sprintf(buf," G"); strcat(ret,buf); } | ||
1612 | else if (g == 2) { sprintf(buf," V"); strcat(ret,buf); } | ||
1613 | else if (g == 4) { sprintf(buf," Z"); strcat(ret,buf); } | ||
1614 | else { sprintf(buf," ."); strcat(ret,buf); } | ||
1615 | } else { | ||
1616 | sprintf(buf," "); strcat(ret,buf); | ||
1617 | } | ||
1618 | } | ||
1619 | sprintf(buf,"\n"); strcat(ret,buf); | ||
1620 | } | ||
1621 | |||
1622 | return ret; | ||
1623 | } | ||
1624 | |||
1625 | struct game_ui { | ||
1626 | int hx, hy; /* as for solo.c, highlight pos */ | ||
1627 | int hshow, hpencil, hcursor; /* show state, type, and ?cursor. */ | ||
1628 | int ascii; | ||
1629 | }; | ||
1630 | |||
1631 | static game_ui *new_ui(const game_state *state) | ||
1632 | { | ||
1633 | game_ui *ui = snew(game_ui); | ||
1634 | ui->hx = ui->hy = 0; | ||
1635 | ui->hpencil = ui->hshow = ui->hcursor = 0; | ||
1636 | ui->ascii = FALSE; | ||
1637 | return ui; | ||
1638 | } | ||
1639 | |||
1640 | static void free_ui(game_ui *ui) { | ||
1641 | sfree(ui); | ||
1642 | return; | ||
1643 | } | ||
1644 | |||
1645 | static char *encode_ui(const game_ui *ui) | ||
1646 | { | ||
1647 | return NULL; | ||
1648 | } | ||
1649 | |||
1650 | static void decode_ui(game_ui *ui, const char *encoding) | ||
1651 | { | ||
1652 | return; | ||
1653 | } | ||
1654 | |||
1655 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | ||
1656 | const game_state *newstate) | ||
1657 | { | ||
1658 | /* See solo.c; if we were pencil-mode highlighting and | ||
1659 | * somehow a square has just been properly filled, cancel | ||
1660 | * pencil mode. */ | ||
1661 | if (ui->hshow && ui->hpencil && !ui->hcursor) { | ||
1662 | int g = newstate->guess[newstate->common->xinfo[ui->hx + ui->hy*(newstate->common->params.w+2)]]; | ||
1663 | if (g == 1 || g == 2 || g == 4) | ||
1664 | ui->hshow = 0; | ||
1665 | } | ||
1666 | } | ||
1667 | |||
1668 | struct game_drawstate { | ||
1669 | int tilesize, started, solved; | ||
1670 | int w, h; | ||
1671 | |||
1672 | int *monsters; | ||
1673 | unsigned char *pencils; | ||
1674 | |||
1675 | unsigned char count_errors[3]; | ||
1676 | unsigned char *cell_errors; | ||
1677 | unsigned char *hint_errors; | ||
1678 | unsigned char *hints_done; | ||
1679 | |||
1680 | int hx, hy, hshow, hpencil; /* as for game_ui. */ | ||
1681 | int hflash; | ||
1682 | int ascii; | ||
1683 | }; | ||
1684 | |||
1685 | static int is_clue(const game_state *state, int x, int y) | ||
1686 | { | ||
1687 | int h = state->common->params.h, w = state->common->params.w; | ||
1688 | |||
1689 | if (((x == 0 || x == w + 1) && y > 0 && y <= h) || | ||
1690 | ((y == 0 || y == h + 1) && x > 0 && x <= w)) | ||
1691 | return TRUE; | ||
1692 | |||
1693 | return FALSE; | ||
1694 | } | ||
1695 | |||
1696 | static int clue_index(const game_state *state, int x, int y) | ||
1697 | { | ||
1698 | int h = state->common->params.h, w = state->common->params.w; | ||
1699 | |||
1700 | if (y == 0) | ||
1701 | return x - 1; | ||
1702 | else if (x == w + 1) | ||
1703 | return w + y - 1; | ||
1704 | else if (y == h + 1) | ||
1705 | return 2 * w + h - x; | ||
1706 | else if (x == 0) | ||
1707 | return 2 * (w + h) - y; | ||
1708 | |||
1709 | return -1; | ||
1710 | } | ||
1711 | |||
1712 | #define TILESIZE (ds->tilesize) | ||
1713 | #define BORDER (TILESIZE/4) | ||
1714 | |||
1715 | static char *interpret_move(const game_state *state, game_ui *ui, | ||
1716 | const game_drawstate *ds, | ||
1717 | int x, int y, int button) | ||
1718 | { | ||
1719 | int gx,gy; | ||
1720 | int g,xi; | ||
1721 | char buf[80]; | ||
1722 | |||
1723 | gx = ((x-BORDER-1) / TILESIZE ); | ||
1724 | gy = ((y-BORDER-2) / TILESIZE ) - 1; | ||
1725 | |||
1726 | if (button == 'a' || button == 'A') { | ||
1727 | ui->ascii = !ui->ascii; | ||
1728 | return ""; | ||
1729 | } | ||
1730 | |||
1731 | if (button == 'm' || button == 'M') { | ||
1732 | return dupstr("M"); | ||
1733 | } | ||
1734 | |||
1735 | if (ui->hshow == 1 && ui->hpencil == 0) { | ||
1736 | xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)]; | ||
1737 | if (xi >= 0 && !state->common->fixed[xi]) { | ||
1738 | if (button == 'g' || button == 'G' || button == '1') { | ||
1739 | if (!ui->hcursor) ui->hshow = 0; | ||
1740 | sprintf(buf,"G%d",xi); | ||
1741 | return dupstr(buf); | ||
1742 | } | ||
1743 | if (button == 'v' || button == 'V' || button == '2') { | ||
1744 | if (!ui->hcursor) ui->hshow = 0; | ||
1745 | sprintf(buf,"V%d",xi); | ||
1746 | return dupstr(buf); | ||
1747 | } | ||
1748 | if (button == 'z' || button == 'Z' || button == '3') { | ||
1749 | if (!ui->hcursor) ui->hshow = 0; | ||
1750 | sprintf(buf,"Z%d",xi); | ||
1751 | return dupstr(buf); | ||
1752 | } | ||
1753 | if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 || | ||
1754 | button == '0' || button == '\b' ) { | ||
1755 | if (!ui->hcursor) ui->hshow = 0; | ||
1756 | sprintf(buf,"E%d",xi); | ||
1757 | return dupstr(buf); | ||
1758 | } | ||
1759 | } | ||
1760 | } | ||
1761 | |||
1762 | if (IS_CURSOR_MOVE(button)) { | ||
1763 | if (ui->hx == 0 && ui->hy == 0) { | ||
1764 | ui->hx = 1; | ||
1765 | ui->hy = 1; | ||
1766 | } | ||
1767 | else switch (button) { | ||
1768 | case CURSOR_UP: ui->hy -= (ui->hy > 1) ? 1 : 0; break; | ||
1769 | case CURSOR_DOWN: ui->hy += (ui->hy < ds->h) ? 1 : 0; break; | ||
1770 | case CURSOR_RIGHT: ui->hx += (ui->hx < ds->w) ? 1 : 0; break; | ||
1771 | case CURSOR_LEFT: ui->hx -= (ui->hx > 1) ? 1 : 0; break; | ||
1772 | } | ||
1773 | ui->hshow = ui->hcursor = 1; | ||
1774 | return ""; | ||
1775 | } | ||
1776 | if (ui->hshow && button == CURSOR_SELECT) { | ||
1777 | ui->hpencil = 1 - ui->hpencil; | ||
1778 | ui->hcursor = 1; | ||
1779 | return ""; | ||
1780 | } | ||
1781 | |||
1782 | if (ui->hshow == 1 && ui->hpencil == 1) { | ||
1783 | xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)]; | ||
1784 | if (xi >= 0 && !state->common->fixed[xi]) { | ||
1785 | if (button == 'g' || button == 'G' || button == '1') { | ||
1786 | sprintf(buf,"g%d",xi); | ||
1787 | if (!ui->hcursor) ui->hpencil = ui->hshow = 0; | ||
1788 | return dupstr(buf); | ||
1789 | } | ||
1790 | if (button == 'v' || button == 'V' || button == '2') { | ||
1791 | sprintf(buf,"v%d",xi); | ||
1792 | if (!ui->hcursor) ui->hpencil = ui->hshow = 0; | ||
1793 | return dupstr(buf); | ||
1794 | } | ||
1795 | if (button == 'z' || button == 'Z' || button == '3') { | ||
1796 | sprintf(buf,"z%d",xi); | ||
1797 | if (!ui->hcursor) ui->hpencil = ui->hshow = 0; | ||
1798 | return dupstr(buf); | ||
1799 | } | ||
1800 | if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 || | ||
1801 | button == '0' || button == '\b') { | ||
1802 | sprintf(buf,"E%d",xi); | ||
1803 | if (!ui->hcursor) ui->hpencil = ui->hshow = 0; | ||
1804 | return dupstr(buf); | ||
1805 | } | ||
1806 | } | ||
1807 | } | ||
1808 | |||
1809 | if (gx > 0 && gx < ds->w+1 && gy > 0 && gy < ds->h+1) { | ||
1810 | xi = state->common->xinfo[gx+gy*(state->common->params.w+2)]; | ||
1811 | if (xi >= 0 && !state->common->fixed[xi]) { | ||
1812 | g = state->guess[xi]; | ||
1813 | if (ui->hshow == 0) { | ||
1814 | if (button == LEFT_BUTTON) { | ||
1815 | ui->hshow = 1; ui->hpencil = 0; ui->hcursor = 0; | ||
1816 | ui->hx = gx; ui->hy = gy; | ||
1817 | return ""; | ||
1818 | } | ||
1819 | else if (button == RIGHT_BUTTON && g == 7) { | ||
1820 | ui->hshow = 1; ui->hpencil = 1; ui->hcursor = 0; | ||
1821 | ui->hx = gx; ui->hy = gy; | ||
1822 | return ""; | ||
1823 | } | ||
1824 | } | ||
1825 | else if (ui->hshow == 1) { | ||
1826 | if (button == LEFT_BUTTON) { | ||
1827 | if (ui->hpencil == 0) { | ||
1828 | if (gx == ui->hx && gy == ui->hy) { | ||
1829 | ui->hshow = 0; ui->hpencil = 0; ui->hcursor = 0; | ||
1830 | ui->hx = 0; ui->hy = 0; | ||
1831 | return ""; | ||
1832 | } | ||
1833 | else { | ||
1834 | ui->hshow = 1; ui->hpencil = 0; ui->hcursor = 0; | ||
1835 | ui->hx = gx; ui->hy = gy; | ||
1836 | return ""; | ||
1837 | } | ||
1838 | } | ||
1839 | else { | ||
1840 | ui->hshow = 1; ui->hpencil = 0; ui->hcursor = 0; | ||
1841 | ui->hx = gx; ui->hy = gy; | ||
1842 | return ""; | ||
1843 | } | ||
1844 | } | ||
1845 | else if (button == RIGHT_BUTTON) { | ||
1846 | if (ui->hpencil == 0 && g == 7) { | ||
1847 | ui->hshow = 1; ui->hpencil = 1; ui->hcursor = 0; | ||
1848 | ui->hx = gx; ui->hy = gy; | ||
1849 | return ""; | ||
1850 | } | ||
1851 | else { | ||
1852 | if (gx == ui->hx && gy == ui->hy) { | ||
1853 | ui->hshow = 0; ui->hpencil = 0; ui->hcursor = 0; | ||
1854 | ui->hx = 0; ui->hy = 0; | ||
1855 | return ""; | ||
1856 | } | ||
1857 | else if (g == 7) { | ||
1858 | ui->hshow = 1; ui->hpencil = 1; ui->hcursor = 0; | ||
1859 | ui->hx = gx; ui->hy = gy; | ||
1860 | return ""; | ||
1861 | } | ||
1862 | } | ||
1863 | } | ||
1864 | } | ||
1865 | } | ||
1866 | } else if (button == LEFT_BUTTON) { | ||
1867 | if (is_clue(state, gx, gy)) { | ||
1868 | sprintf(buf, "D%d,%d", gx, gy); | ||
1869 | return dupstr(buf); | ||
1870 | } | ||
1871 | } | ||
1872 | |||
1873 | return NULL; | ||
1874 | } | ||
1875 | |||
1876 | int check_numbers_draw(game_state *state, int *guess) { | ||
1877 | int valid, filled; | ||
1878 | int i,x,y,xy; | ||
1879 | int count_ghosts, count_vampires, count_zombies; | ||
1880 | |||
1881 | count_ghosts = count_vampires = count_zombies = 0; | ||
1882 | for (i=0;i<state->common->num_total;i++) { | ||
1883 | if (guess[i] == 1) count_ghosts++; | ||
1884 | if (guess[i] == 2) count_vampires++; | ||
1885 | if (guess[i] == 4) count_zombies++; | ||
1886 | } | ||
1887 | |||
1888 | valid = TRUE; | ||
1889 | filled = (count_ghosts + count_vampires + count_zombies >= | ||
1890 | state->common->num_total); | ||
1891 | |||
1892 | if (count_ghosts > state->common->num_ghosts || | ||
1893 | (filled && count_ghosts != state->common->num_ghosts) ) { | ||
1894 | valid = FALSE; | ||
1895 | state->count_errors[0] = TRUE; | ||
1896 | for (x=1;x<state->common->params.w+1;x++) | ||
1897 | for (y=1;y<state->common->params.h+1;y++) { | ||
1898 | xy = x+y*(state->common->params.w+2); | ||
1899 | if (state->common->xinfo[xy] >= 0 && | ||
1900 | guess[state->common->xinfo[xy]] == 1) | ||
1901 | state->cell_errors[xy] = TRUE; | ||
1902 | } | ||
1903 | } | ||
1904 | if (count_vampires > state->common->num_vampires || | ||
1905 | (filled && count_vampires != state->common->num_vampires) ) { | ||
1906 | valid = FALSE; | ||
1907 | state->count_errors[1] = TRUE; | ||
1908 | for (x=1;x<state->common->params.w+1;x++) | ||
1909 | for (y=1;y<state->common->params.h+1;y++) { | ||
1910 | xy = x+y*(state->common->params.w+2); | ||
1911 | if (state->common->xinfo[xy] >= 0 && | ||
1912 | guess[state->common->xinfo[xy]] == 2) | ||
1913 | state->cell_errors[xy] = TRUE; | ||
1914 | } | ||
1915 | } | ||
1916 | if (count_zombies > state->common->num_zombies || | ||
1917 | (filled && count_zombies != state->common->num_zombies) ) { | ||
1918 | valid = FALSE; | ||
1919 | state->count_errors[2] = TRUE; | ||
1920 | for (x=1;x<state->common->params.w+1;x++) | ||
1921 | for (y=1;y<state->common->params.h+1;y++) { | ||
1922 | xy = x+y*(state->common->params.w+2); | ||
1923 | if (state->common->xinfo[xy] >= 0 && | ||
1924 | guess[state->common->xinfo[xy]] == 4) | ||
1925 | state->cell_errors[xy] = TRUE; | ||
1926 | } | ||
1927 | } | ||
1928 | |||
1929 | return valid; | ||
1930 | } | ||
1931 | |||
1932 | int check_path_solution(game_state *state, int p) { | ||
1933 | int i; | ||
1934 | int mirror; | ||
1935 | int count; | ||
1936 | int correct; | ||
1937 | int unfilled; | ||
1938 | |||
1939 | count = 0; | ||
1940 | mirror = FALSE; | ||
1941 | correct = TRUE; | ||
1942 | |||
1943 | unfilled = 0; | ||
1944 | for (i=0;i<state->common->paths[p].length;i++) { | ||
1945 | if (state->common->paths[p].p[i] == -1) mirror = TRUE; | ||
1946 | else { | ||
1947 | if (state->guess[state->common->paths[p].p[i]] == 1 && mirror) | ||
1948 | count++; | ||
1949 | else if (state->guess[state->common->paths[p].p[i]] == 2 && !mirror) | ||
1950 | count++; | ||
1951 | else if (state->guess[state->common->paths[p].p[i]] == 4) | ||
1952 | count++; | ||
1953 | else if (state->guess[state->common->paths[p].p[i]] == 7) | ||
1954 | unfilled++; | ||
1955 | } | ||
1956 | } | ||
1957 | |||
1958 | if (count > state->common->paths[p].sightings_start || | ||
1959 | count + unfilled < state->common->paths[p].sightings_start) | ||
1960 | { | ||
1961 | correct = FALSE; | ||
1962 | state->hint_errors[state->common->paths[p].grid_start] = TRUE; | ||
1963 | } | ||
1964 | |||
1965 | count = 0; | ||
1966 | mirror = FALSE; | ||
1967 | unfilled = 0; | ||
1968 | for (i=state->common->paths[p].length-1;i>=0;i--) { | ||
1969 | if (state->common->paths[p].p[i] == -1) mirror = TRUE; | ||
1970 | else { | ||
1971 | if (state->guess[state->common->paths[p].p[i]] == 1 && mirror) | ||
1972 | count++; | ||
1973 | else if (state->guess[state->common->paths[p].p[i]] == 2 && !mirror) | ||
1974 | count++; | ||
1975 | else if (state->guess[state->common->paths[p].p[i]] == 4) | ||
1976 | count++; | ||
1977 | else if (state->guess[state->common->paths[p].p[i]] == 7) | ||
1978 | unfilled++; | ||
1979 | } | ||
1980 | } | ||
1981 | |||
1982 | if (count > state->common->paths[p].sightings_end || | ||
1983 | count + unfilled < state->common->paths[p].sightings_end) | ||
1984 | { | ||
1985 | correct = FALSE; | ||
1986 | state->hint_errors[state->common->paths[p].grid_end] = TRUE; | ||
1987 | } | ||
1988 | |||
1989 | if (!correct) { | ||
1990 | for (i=0;i<state->common->paths[p].length;i++) | ||
1991 | state->cell_errors[state->common->paths[p].xy[i]] = TRUE; | ||
1992 | } | ||
1993 | |||
1994 | return correct; | ||
1995 | } | ||
1996 | |||
1997 | static game_state *execute_move(const game_state *state, const char *move) | ||
1998 | { | ||
1999 | int x,y,n,p,i; | ||
2000 | char c; | ||
2001 | int correct; | ||
2002 | int solver; | ||
2003 | |||
2004 | game_state *ret = dup_game(state); | ||
2005 | solver = FALSE; | ||
2006 | |||
2007 | while (*move) { | ||
2008 | c = *move; | ||
2009 | if (c == 'S') { | ||
2010 | move++; | ||
2011 | solver = TRUE; | ||
2012 | } | ||
2013 | if (c == 'G' || c == 'V' || c == 'Z' || c == 'E' || | ||
2014 | c == 'g' || c == 'v' || c == 'z') { | ||
2015 | move++; | ||
2016 | sscanf(move, "%d%n", &x, &n); | ||
2017 | if (c == 'G') ret->guess[x] = 1; | ||
2018 | if (c == 'V') ret->guess[x] = 2; | ||
2019 | if (c == 'Z') ret->guess[x] = 4; | ||
2020 | if (c == 'E') { ret->guess[x] = 7; ret->pencils[x] = 0; } | ||
2021 | if (c == 'g') ret->pencils[x] ^= 1; | ||
2022 | if (c == 'v') ret->pencils[x] ^= 2; | ||
2023 | if (c == 'z') ret->pencils[x] ^= 4; | ||
2024 | move += n; | ||
2025 | } | ||
2026 | if (c == 'D' && sscanf(move + 1, "%d,%d%n", &x, &y, &n) == 2 && | ||
2027 | is_clue(ret, x, y)) { | ||
2028 | ret->hints_done[clue_index(ret, x, y)] ^= 1; | ||
2029 | move += n + 1; | ||
2030 | } | ||
2031 | if (c == 'M') { | ||
2032 | /* | ||
2033 | * Fill in absolutely all pencil marks in unfilled | ||
2034 | * squares, for those who like to play by the rigorous | ||
2035 | * approach of starting off in that state and eliminating | ||
2036 | * things. | ||
2037 | */ | ||
2038 | for (i = 0; i < ret->common->wh; i++) | ||
2039 | if (ret->guess[i] == 7) | ||
2040 | ret->pencils[i] = 7; | ||
2041 | move++; | ||
2042 | } | ||
2043 | if (*move == ';') move++; | ||
2044 | } | ||
2045 | |||
2046 | correct = TRUE; | ||
2047 | |||
2048 | for (i=0;i<ret->common->wh;i++) ret->cell_errors[i] = FALSE; | ||
2049 | for (i=0;i<2*ret->common->num_paths;i++) ret->hint_errors[i] = FALSE; | ||
2050 | for (i=0;i<3;i++) ret->count_errors[i] = FALSE; | ||
2051 | |||
2052 | if (!check_numbers_draw(ret,ret->guess)) correct = FALSE; | ||
2053 | |||
2054 | for (p=0;p<state->common->num_paths;p++) | ||
2055 | if (!check_path_solution(ret,p)) correct = FALSE; | ||
2056 | |||
2057 | for (i=0;i<state->common->num_total;i++) | ||
2058 | if (!(ret->guess[i] == 1 || ret->guess[i] == 2 || | ||
2059 | ret->guess[i] == 4)) correct = FALSE; | ||
2060 | |||
2061 | if (correct && !solver) ret->solved = TRUE; | ||
2062 | if (solver) ret->cheated = TRUE; | ||
2063 | |||
2064 | return ret; | ||
2065 | } | ||
2066 | |||
2067 | /* ---------------------------------------------------------------------- | ||
2068 | * Drawing routines. | ||
2069 | */ | ||
2070 | |||
2071 | #define PREFERRED_TILE_SIZE 64 | ||
2072 | |||
2073 | static void game_compute_size(const game_params *params, int tilesize, | ||
2074 | int *x, int *y) | ||
2075 | { | ||
2076 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ | ||
2077 | struct { int tilesize; } ads, *ds = &ads; | ||
2078 | ads.tilesize = tilesize; | ||
2079 | |||
2080 | *x = 2*BORDER+(params->w+2)*TILESIZE; | ||
2081 | *y = 2*BORDER+(params->h+3)*TILESIZE; | ||
2082 | return; | ||
2083 | } | ||
2084 | |||
2085 | static void game_set_size(drawing *dr, game_drawstate *ds, | ||
2086 | const game_params *params, int tilesize) | ||
2087 | { | ||
2088 | ds->tilesize = tilesize; | ||
2089 | return; | ||
2090 | } | ||
2091 | |||
2092 | #define COLOUR(ret, i, r, g, b) ((ret[3*(i)+0] = (r)), (ret[3*(i)+1] = (g)), (ret[3*(i)+2] = (b))) | ||
2093 | |||
2094 | static float *game_colours(frontend *fe, int *ncolours) | ||
2095 | { | ||
2096 | float *ret = snewn(3 * NCOLOURS, float); | ||
2097 | |||
2098 | frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); | ||
2099 | |||
2100 | ret[COL_GRID * 3 + 0] = 0.0F; | ||
2101 | ret[COL_GRID * 3 + 1] = 0.0F; | ||
2102 | ret[COL_GRID * 3 + 2] = 0.0F; | ||
2103 | |||
2104 | ret[COL_TEXT * 3 + 0] = 0.0F; | ||
2105 | ret[COL_TEXT * 3 + 1] = 0.0F; | ||
2106 | ret[COL_TEXT * 3 + 2] = 0.0F; | ||
2107 | |||
2108 | ret[COL_ERROR * 3 + 0] = 1.0F; | ||
2109 | ret[COL_ERROR * 3 + 1] = 0.0F; | ||
2110 | ret[COL_ERROR * 3 + 2] = 0.0F; | ||
2111 | |||
2112 | ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0]; | ||
2113 | ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1]; | ||
2114 | ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2]; | ||
2115 | |||
2116 | ret[COL_FLASH * 3 + 0] = 1.0F; | ||
2117 | ret[COL_FLASH * 3 + 1] = 1.0F; | ||
2118 | ret[COL_FLASH * 3 + 2] = 1.0F; | ||
2119 | |||
2120 | ret[COL_GHOST * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.5F; | ||
2121 | ret[COL_GHOST * 3 + 1] = ret[COL_BACKGROUND * 3 + 0]; | ||
2122 | ret[COL_GHOST * 3 + 2] = ret[COL_BACKGROUND * 3 + 0]; | ||
2123 | |||
2124 | ret[COL_ZOMBIE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.5F; | ||
2125 | ret[COL_ZOMBIE * 3 + 1] = ret[COL_BACKGROUND * 3 + 0]; | ||
2126 | ret[COL_ZOMBIE * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.5F; | ||
2127 | |||
2128 | ret[COL_VAMPIRE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0]; | ||
2129 | ret[COL_VAMPIRE * 3 + 1] = ret[COL_BACKGROUND * 3 + 0] * 0.9F; | ||
2130 | ret[COL_VAMPIRE * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.9F; | ||
2131 | |||
2132 | ret[COL_DONE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] / 1.5F; | ||
2133 | ret[COL_DONE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] / 1.5F; | ||
2134 | ret[COL_DONE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] / 1.5F; | ||
2135 | |||
2136 | *ncolours = NCOLOURS; | ||
2137 | return ret; | ||
2138 | } | ||
2139 | |||
2140 | static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) | ||
2141 | { | ||
2142 | int i; | ||
2143 | struct game_drawstate *ds = snew(struct game_drawstate); | ||
2144 | |||
2145 | ds->tilesize = 0; | ||
2146 | ds->started = ds->solved = FALSE; | ||
2147 | ds->w = state->common->params.w; | ||
2148 | ds->h = state->common->params.h; | ||
2149 | ds->ascii = FALSE; | ||
2150 | |||
2151 | ds->count_errors[0] = FALSE; | ||
2152 | ds->count_errors[1] = FALSE; | ||
2153 | ds->count_errors[2] = FALSE; | ||
2154 | |||
2155 | ds->monsters = snewn(state->common->num_total,int); | ||
2156 | for (i=0;i<(state->common->num_total);i++) | ||
2157 | ds->monsters[i] = 7; | ||
2158 | ds->pencils = snewn(state->common->num_total,unsigned char); | ||
2159 | for (i=0;i<state->common->num_total;i++) | ||
2160 | ds->pencils[i] = 0; | ||
2161 | |||
2162 | ds->cell_errors = snewn(state->common->wh,unsigned char); | ||
2163 | for (i=0;i<state->common->wh;i++) | ||
2164 | ds->cell_errors[i] = FALSE; | ||
2165 | ds->hint_errors = snewn(2*state->common->num_paths,unsigned char); | ||
2166 | for (i=0;i<2*state->common->num_paths;i++) | ||
2167 | ds->hint_errors[i] = FALSE; | ||
2168 | ds->hints_done = snewn(2 * state->common->num_paths, unsigned char); | ||
2169 | memset(ds->hints_done, 0, | ||
2170 | 2 * state->common->num_paths * sizeof(unsigned char)); | ||
2171 | |||
2172 | ds->hshow = ds->hpencil = ds->hflash = 0; | ||
2173 | ds->hx = ds->hy = 0; | ||
2174 | return ds; | ||
2175 | } | ||
2176 | |||
2177 | static void game_free_drawstate(drawing *dr, game_drawstate *ds) { | ||
2178 | sfree(ds->hints_done); | ||
2179 | sfree(ds->hint_errors); | ||
2180 | sfree(ds->cell_errors); | ||
2181 | sfree(ds->pencils); | ||
2182 | sfree(ds->monsters); | ||
2183 | sfree(ds); | ||
2184 | return; | ||
2185 | } | ||
2186 | |||
2187 | static void draw_cell_background(drawing *dr, game_drawstate *ds, | ||
2188 | const game_state *state, const game_ui *ui, | ||
2189 | int x, int y) { | ||
2190 | |||
2191 | int hon; | ||
2192 | int dx,dy; | ||
2193 | dx = BORDER+(x* ds->tilesize)+(TILESIZE/2); | ||
2194 | dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE; | ||
2195 | |||
2196 | hon = (ui->hshow && x == ui->hx && y == ui->hy); | ||
2197 | draw_rect(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1,(hon && !ui->hpencil) ? COL_HIGHLIGHT : COL_BACKGROUND); | ||
2198 | |||
2199 | if (hon && ui->hpencil) { | ||
2200 | int coords[6]; | ||
2201 | coords[0] = dx-(TILESIZE/2)+1; | ||
2202 | coords[1] = dy-(TILESIZE/2)+1; | ||
2203 | coords[2] = coords[0] + TILESIZE/2; | ||
2204 | coords[3] = coords[1]; | ||
2205 | coords[4] = coords[0]; | ||
2206 | coords[5] = coords[1] + TILESIZE/2; | ||
2207 | draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); | ||
2208 | } | ||
2209 | |||
2210 | draw_update(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1); | ||
2211 | |||
2212 | return; | ||
2213 | } | ||
2214 | |||
2215 | static void draw_circle_or_point(drawing *dr, int cx, int cy, int radius, | ||
2216 | int colour) | ||
2217 | { | ||
2218 | if (radius > 0) | ||
2219 | draw_circle(dr, cx, cy, radius, colour, colour); | ||
2220 | else | ||
2221 | draw_rect(dr, cx, cy, 1, 1, colour); | ||
2222 | } | ||
2223 | |||
2224 | static void draw_monster(drawing *dr, game_drawstate *ds, int x, int y, | ||
2225 | int tilesize, int hflash, int monster) | ||
2226 | { | ||
2227 | int black = (hflash ? COL_FLASH : COL_TEXT); | ||
2228 | |||
2229 | if (monster == 1) { /* ghost */ | ||
2230 | int poly[80], i, j; | ||
2231 | |||
2232 | clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize/2+1); | ||
2233 | draw_circle(dr,x,y,2*tilesize/5, COL_GHOST,black); | ||
2234 | unclip(dr); | ||
2235 | |||
2236 | i = 0; | ||
2237 | poly[i++] = x - 2*tilesize/5; | ||
2238 | poly[i++] = y-2; | ||
2239 | poly[i++] = x - 2*tilesize/5; | ||
2240 | poly[i++] = y + 2*tilesize/5; | ||
2241 | |||
2242 | for (j = 0; j < 3; j++) { | ||
2243 | int total = (2*tilesize/5) * 2; | ||
2244 | int before = total * j / 3; | ||
2245 | int after = total * (j+1) / 3; | ||
2246 | int mid = (before + after) / 2; | ||
2247 | poly[i++] = x - 2*tilesize/5 + mid; | ||
2248 | poly[i++] = y + 2*tilesize/5 - (total / 6); | ||
2249 | poly[i++] = x - 2*tilesize/5 + after; | ||
2250 | poly[i++] = y + 2*tilesize/5; | ||
2251 | } | ||
2252 | |||
2253 | poly[i++] = x + 2*tilesize/5; | ||
2254 | poly[i++] = y-2; | ||
2255 | |||
2256 | clip(dr,x-(tilesize/2)+2,y,tilesize-3,tilesize-(tilesize/2)-1); | ||
2257 | draw_polygon(dr, poly, i/2, COL_GHOST, black); | ||
2258 | unclip(dr); | ||
2259 | |||
2260 | draw_circle(dr,x-tilesize/6,y-tilesize/12,tilesize/10, | ||
2261 | COL_BACKGROUND,black); | ||
2262 | draw_circle(dr,x+tilesize/6,y-tilesize/12,tilesize/10, | ||
2263 | COL_BACKGROUND,black); | ||
2264 | |||
2265 | draw_circle_or_point(dr,x-tilesize/6+1+tilesize/48,y-tilesize/12, | ||
2266 | tilesize/48,black); | ||
2267 | draw_circle_or_point(dr,x+tilesize/6+1+tilesize/48,y-tilesize/12, | ||
2268 | tilesize/48,black); | ||
2269 | |||
2270 | } else if (monster == 2) { /* vampire */ | ||
2271 | int poly[80], i; | ||
2272 | |||
2273 | clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize/2); | ||
2274 | draw_circle(dr,x,y,2*tilesize/5,black,black); | ||
2275 | unclip(dr); | ||
2276 | |||
2277 | clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize/2+1,tilesize/2); | ||
2278 | draw_circle(dr,x-tilesize/7,y,2*tilesize/5-tilesize/7, | ||
2279 | COL_VAMPIRE,black); | ||
2280 | unclip(dr); | ||
2281 | clip(dr,x,y-(tilesize/2)+2,tilesize/2+1,tilesize/2); | ||
2282 | draw_circle(dr,x+tilesize/7,y,2*tilesize/5-tilesize/7, | ||
2283 | COL_VAMPIRE,black); | ||
2284 | unclip(dr); | ||
2285 | |||
2286 | clip(dr,x-(tilesize/2)+2,y,tilesize-3,tilesize/2); | ||
2287 | draw_circle(dr,x,y,2*tilesize/5, COL_VAMPIRE,black); | ||
2288 | unclip(dr); | ||
2289 | |||
2290 | draw_circle(dr, x-tilesize/7, y-tilesize/16, tilesize/16, | ||
2291 | COL_BACKGROUND, black); | ||
2292 | draw_circle(dr, x+tilesize/7, y-tilesize/16, tilesize/16, | ||
2293 | COL_BACKGROUND, black); | ||
2294 | draw_circle_or_point(dr, x-tilesize/7, y-tilesize/16, tilesize/48, | ||
2295 | black); | ||
2296 | draw_circle_or_point(dr, x+tilesize/7, y-tilesize/16, tilesize/48, | ||
2297 | black); | ||
2298 | |||
2299 | clip(dr, x-(tilesize/2)+2, y+tilesize/8, tilesize-3, tilesize/4); | ||
2300 | |||
2301 | i = 0; | ||
2302 | poly[i++] = x-3*tilesize/16; | ||
2303 | poly[i++] = y+1*tilesize/8; | ||
2304 | poly[i++] = x-2*tilesize/16; | ||
2305 | poly[i++] = y+7*tilesize/24; | ||
2306 | poly[i++] = x-1*tilesize/16; | ||
2307 | poly[i++] = y+1*tilesize/8; | ||
2308 | draw_polygon(dr, poly, i/2, COL_BACKGROUND, black); | ||
2309 | i = 0; | ||
2310 | poly[i++] = x+3*tilesize/16; | ||
2311 | poly[i++] = y+1*tilesize/8; | ||
2312 | poly[i++] = x+2*tilesize/16; | ||
2313 | poly[i++] = y+7*tilesize/24; | ||
2314 | poly[i++] = x+1*tilesize/16; | ||
2315 | poly[i++] = y+1*tilesize/8; | ||
2316 | draw_polygon(dr, poly, i/2, COL_BACKGROUND, black); | ||
2317 | |||
2318 | draw_circle(dr, x, y-tilesize/5, 2*tilesize/5, COL_VAMPIRE, black); | ||
2319 | unclip(dr); | ||
2320 | |||
2321 | } else if (monster == 4) { /* zombie */ | ||
2322 | draw_circle(dr,x,y,2*tilesize/5, COL_ZOMBIE,black); | ||
2323 | |||
2324 | draw_line(dr, | ||
2325 | x-tilesize/7-tilesize/16, y-tilesize/12-tilesize/16, | ||
2326 | x-tilesize/7+tilesize/16, y-tilesize/12+tilesize/16, | ||
2327 | black); | ||
2328 | draw_line(dr, | ||
2329 | x-tilesize/7+tilesize/16, y-tilesize/12-tilesize/16, | ||
2330 | x-tilesize/7-tilesize/16, y-tilesize/12+tilesize/16, | ||
2331 | black); | ||
2332 | draw_line(dr, | ||
2333 | x+tilesize/7-tilesize/16, y-tilesize/12-tilesize/16, | ||
2334 | x+tilesize/7+tilesize/16, y-tilesize/12+tilesize/16, | ||
2335 | black); | ||
2336 | draw_line(dr, | ||
2337 | x+tilesize/7+tilesize/16, y-tilesize/12-tilesize/16, | ||
2338 | x+tilesize/7-tilesize/16, y-tilesize/12+tilesize/16, | ||
2339 | black); | ||
2340 | |||
2341 | clip(dr, x-tilesize/5, y+tilesize/6, 2*tilesize/5+1, tilesize/2); | ||
2342 | draw_circle(dr, x-tilesize/15, y+tilesize/6, tilesize/12, | ||
2343 | COL_BACKGROUND, black); | ||
2344 | unclip(dr); | ||
2345 | |||
2346 | draw_line(dr, x-tilesize/5, y+tilesize/6, x+tilesize/5, y+tilesize/6, | ||
2347 | black); | ||
2348 | } | ||
2349 | |||
2350 | draw_update(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize-3); | ||
2351 | } | ||
2352 | |||
2353 | static void draw_monster_count(drawing *dr, game_drawstate *ds, | ||
2354 | const game_state *state, int c, int hflash) { | ||
2355 | int dx,dy; | ||
2356 | char buf[8]; | ||
2357 | char bufm[8]; | ||
2358 | |||
2359 | dy = TILESIZE/4; | ||
2360 | dx = BORDER+(ds->w+2)*TILESIZE/2+TILESIZE/4; | ||
2361 | switch (c) { | ||
2362 | case 0: | ||
2363 | sprintf(buf,"%d",state->common->num_ghosts); | ||
2364 | sprintf(bufm,"G"); | ||
2365 | dx -= 3*TILESIZE/2; | ||
2366 | break; | ||
2367 | case 1: | ||
2368 | sprintf(buf,"%d",state->common->num_vampires); | ||
2369 | sprintf(bufm,"V"); | ||
2370 | break; | ||
2371 | case 2: | ||
2372 | sprintf(buf,"%d",state->common->num_zombies); | ||
2373 | sprintf(bufm,"Z"); | ||
2374 | dx += 3*TILESIZE/2; | ||
2375 | break; | ||
2376 | } | ||
2377 | |||
2378 | draw_rect(dr, dx-2*TILESIZE/3, dy, 3*TILESIZE/2, TILESIZE, | ||
2379 | COL_BACKGROUND); | ||
2380 | if (!ds->ascii) { | ||
2381 | draw_monster(dr, ds, dx-TILESIZE/3, dy+TILESIZE/2, | ||
2382 | 2*TILESIZE/3, hflash, 1<<c); | ||
2383 | } else { | ||
2384 | draw_text(dr, dx-TILESIZE/3,dy+TILESIZE/2,FONT_VARIABLE,TILESIZE/2, | ||
2385 | ALIGN_HCENTRE|ALIGN_VCENTRE, | ||
2386 | hflash ? COL_FLASH : COL_TEXT, bufm); | ||
2387 | } | ||
2388 | draw_text(dr, dx, dy+TILESIZE/2, FONT_VARIABLE, TILESIZE/2, | ||
2389 | ALIGN_HLEFT|ALIGN_VCENTRE, | ||
2390 | (state->count_errors[c] ? COL_ERROR : | ||
2391 | hflash ? COL_FLASH : COL_TEXT), buf); | ||
2392 | draw_update(dr, dx-2*TILESIZE/3, dy, 3*TILESIZE/2, TILESIZE); | ||
2393 | |||
2394 | return; | ||
2395 | } | ||
2396 | |||
2397 | static void draw_path_hint(drawing *dr, game_drawstate *ds, | ||
2398 | const struct game_params *params, | ||
2399 | int hint_index, int hflash, int hint) { | ||
2400 | int x, y, color, dx, dy, text_dx, text_dy, text_size; | ||
2401 | char buf[4]; | ||
2402 | |||
2403 | if (ds->hint_errors[hint_index]) | ||
2404 | color = COL_ERROR; | ||
2405 | else if (hflash) | ||
2406 | color = COL_FLASH; | ||
2407 | else if (ds->hints_done[hint_index]) | ||
2408 | color = COL_DONE; | ||
2409 | else | ||
2410 | color = COL_TEXT; | ||
2411 | |||
2412 | range2grid(hint_index, params->w, params->h, &x, &y); | ||
2413 | /* Upper-left corner of the "tile" */ | ||
2414 | dx = BORDER + x * TILESIZE; | ||
2415 | dy = BORDER + y * TILESIZE + TILESIZE; | ||
2416 | /* Center of the "tile" */ | ||
2417 | text_dx = dx + TILESIZE / 2; | ||
2418 | text_dy = dy + TILESIZE / 2; | ||
2419 | /* Avoid wiping out the borders of the puzzle */ | ||
2420 | dx += 2; | ||
2421 | dy += 2; | ||
2422 | text_size = TILESIZE - 3; | ||
2423 | |||
2424 | sprintf(buf,"%d", hint); | ||
2425 | draw_rect(dr, dx, dy, text_size, text_size, COL_BACKGROUND); | ||
2426 | draw_text(dr, text_dx, text_dy, FONT_FIXED, TILESIZE / 2, | ||
2427 | ALIGN_HCENTRE | ALIGN_VCENTRE, color, buf); | ||
2428 | draw_update(dr, dx, dy, text_size, text_size); | ||
2429 | |||
2430 | return; | ||
2431 | } | ||
2432 | |||
2433 | static void draw_mirror(drawing *dr, game_drawstate *ds, | ||
2434 | const game_state *state, int x, int y, | ||
2435 | int hflash, int mirror) { | ||
2436 | int dx,dy,mx1,my1,mx2,my2; | ||
2437 | dx = BORDER+(x* ds->tilesize)+(TILESIZE/2); | ||
2438 | dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE; | ||
2439 | |||
2440 | if (mirror == CELL_MIRROR_L) { | ||
2441 | mx1 = dx-(TILESIZE/4); | ||
2442 | my1 = dy-(TILESIZE/4); | ||
2443 | mx2 = dx+(TILESIZE/4); | ||
2444 | my2 = dy+(TILESIZE/4); | ||
2445 | } | ||
2446 | else { | ||
2447 | mx1 = dx-(TILESIZE/4); | ||
2448 | my1 = dy+(TILESIZE/4); | ||
2449 | mx2 = dx+(TILESIZE/4); | ||
2450 | my2 = dy-(TILESIZE/4); | ||
2451 | } | ||
2452 | draw_thick_line(dr,(float)(TILESIZE/16),mx1,my1,mx2,my2, | ||
2453 | hflash ? COL_FLASH : COL_TEXT); | ||
2454 | draw_update(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1); | ||
2455 | |||
2456 | return; | ||
2457 | } | ||
2458 | |||
2459 | static void draw_big_monster(drawing *dr, game_drawstate *ds, | ||
2460 | const game_state *state, int x, int y, | ||
2461 | int hflash, int monster) | ||
2462 | { | ||
2463 | int dx,dy; | ||
2464 | char buf[10]; | ||
2465 | dx = BORDER+(x* ds->tilesize)+(TILESIZE/2); | ||
2466 | dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE; | ||
2467 | if (ds->ascii) { | ||
2468 | if (monster == 1) sprintf(buf,"G"); | ||
2469 | else if (monster == 2) sprintf(buf,"V"); | ||
2470 | else if (monster == 4) sprintf(buf,"Z"); | ||
2471 | else sprintf(buf," "); | ||
2472 | draw_text(dr,dx,dy,FONT_FIXED,TILESIZE/2,ALIGN_HCENTRE|ALIGN_VCENTRE, | ||
2473 | hflash ? COL_FLASH : COL_TEXT,buf); | ||
2474 | draw_update(dr,dx-(TILESIZE/2)+2,dy-(TILESIZE/2)+2,TILESIZE-3, | ||
2475 | TILESIZE-3); | ||
2476 | } | ||
2477 | else { | ||
2478 | draw_monster(dr, ds, dx, dy, 3*TILESIZE/4, hflash, monster); | ||
2479 | } | ||
2480 | return; | ||
2481 | } | ||
2482 | |||
2483 | static void draw_pencils(drawing *dr, game_drawstate *ds, | ||
2484 | const game_state *state, int x, int y, int pencil) | ||
2485 | { | ||
2486 | int dx, dy; | ||
2487 | int monsters[4]; | ||
2488 | int i, j, px, py; | ||
2489 | char buf[10]; | ||
2490 | dx = BORDER+(x* ds->tilesize)+(TILESIZE/4); | ||
2491 | dy = BORDER+(y* ds->tilesize)+(TILESIZE/4)+TILESIZE; | ||
2492 | |||
2493 | for (i = 0, j = 1; j < 8; j *= 2) | ||
2494 | if (pencil & j) | ||
2495 | monsters[i++] = j; | ||
2496 | while (i < 4) | ||
2497 | monsters[i++] = 0; | ||
2498 | |||
2499 | for (py = 0; py < 2; py++) | ||
2500 | for (px = 0; px < 2; px++) | ||
2501 | if (monsters[py*2+px]) { | ||
2502 | if (!ds->ascii) { | ||
2503 | draw_monster(dr, ds, | ||
2504 | dx + TILESIZE/2 * px, dy + TILESIZE/2 * py, | ||
2505 | TILESIZE/2, 0, monsters[py*2+px]); | ||
2506 | } | ||
2507 | else { | ||
2508 | switch (monsters[py*2+px]) { | ||
2509 | case 1: sprintf(buf,"G"); break; | ||
2510 | case 2: sprintf(buf,"V"); break; | ||
2511 | case 4: sprintf(buf,"Z"); break; | ||
2512 | } | ||
2513 | draw_text(dr,dx + TILESIZE/2 * px,dy + TILESIZE/2 * py, | ||
2514 | FONT_FIXED,TILESIZE/4,ALIGN_HCENTRE|ALIGN_VCENTRE, | ||
2515 | COL_TEXT,buf); | ||
2516 | } | ||
2517 | } | ||
2518 | draw_update(dr,dx-(TILESIZE/4)+2,dy-(TILESIZE/4)+2, | ||
2519 | (TILESIZE/2)-3,(TILESIZE/2)-3); | ||
2520 | |||
2521 | return; | ||
2522 | } | ||
2523 | |||
2524 | #define FLASH_TIME 0.7F | ||
2525 | |||
2526 | static int is_hint_stale(const game_drawstate *ds, int hflash, | ||
2527 | const game_state *state, int index) | ||
2528 | { | ||
2529 | int ret = FALSE; | ||
2530 | if (!ds->started) ret = TRUE; | ||
2531 | if (ds->hflash != hflash) ret = TRUE; | ||
2532 | |||
2533 | if (ds->hint_errors[index] != state->hint_errors[index]) { | ||
2534 | ds->hint_errors[index] = state->hint_errors[index]; | ||
2535 | ret = TRUE; | ||
2536 | } | ||
2537 | |||
2538 | if (ds->hints_done[index] != state->hints_done[index]) { | ||
2539 | ds->hints_done[index] = state->hints_done[index]; | ||
2540 | ret = TRUE; | ||
2541 | } | ||
2542 | |||
2543 | return ret; | ||
2544 | } | ||
2545 | |||
2546 | static void game_redraw(drawing *dr, game_drawstate *ds, | ||
2547 | const game_state *oldstate, const game_state *state, | ||
2548 | int dir, const game_ui *ui, | ||
2549 | float animtime, float flashtime) | ||
2550 | { | ||
2551 | int i,j,x,y,xy; | ||
2552 | int stale, xi, c, hflash, hchanged, changed_ascii; | ||
2553 | |||
2554 | hflash = (int)(flashtime * 5 / FLASH_TIME) % 2; | ||
2555 | |||
2556 | /* Draw static grid components at startup */ | ||
2557 | if (!ds->started) { | ||
2558 | draw_rect(dr, 0, 0, 2*BORDER+(ds->w+2)*TILESIZE, | ||
2559 | 2*BORDER+(ds->h+3)*TILESIZE, COL_BACKGROUND); | ||
2560 | draw_rect(dr, BORDER+TILESIZE-1, BORDER+2*TILESIZE-1, | ||
2561 | (ds->w)*TILESIZE +3, (ds->h)*TILESIZE +3, COL_GRID); | ||
2562 | for (i=0;i<ds->w;i++) | ||
2563 | for (j=0;j<ds->h;j++) | ||
2564 | draw_rect(dr, BORDER+(ds->tilesize*(i+1))+1, | ||
2565 | BORDER+(ds->tilesize*(j+2))+1, ds->tilesize-1, | ||
2566 | ds->tilesize-1, COL_BACKGROUND); | ||
2567 | draw_update(dr, 0, 0, 2*BORDER+(ds->w+2)*TILESIZE, | ||
2568 | 2*BORDER+(ds->h+3)*TILESIZE); | ||
2569 | } | ||
2570 | |||
2571 | hchanged = FALSE; | ||
2572 | if (ds->hx != ui->hx || ds->hy != ui->hy || | ||
2573 | ds->hshow != ui->hshow || ds->hpencil != ui->hpencil) | ||
2574 | hchanged = TRUE; | ||
2575 | |||
2576 | if (ds->ascii != ui->ascii) { | ||
2577 | ds->ascii = ui->ascii; | ||
2578 | changed_ascii = TRUE; | ||
2579 | } else | ||
2580 | changed_ascii = FALSE; | ||
2581 | |||
2582 | /* Draw monster count hints */ | ||
2583 | |||
2584 | for (i=0;i<3;i++) { | ||
2585 | stale = FALSE; | ||
2586 | if (!ds->started) stale = TRUE; | ||
2587 | if (ds->hflash != hflash) stale = TRUE; | ||
2588 | if (changed_ascii) stale = TRUE; | ||
2589 | if (ds->count_errors[i] != state->count_errors[i]) { | ||
2590 | stale = TRUE; | ||
2591 | ds->count_errors[i] = state->count_errors[i]; | ||
2592 | } | ||
2593 | |||
2594 | if (stale) { | ||
2595 | draw_monster_count(dr, ds, state, i, hflash); | ||
2596 | } | ||
2597 | } | ||
2598 | |||
2599 | /* Draw path count hints */ | ||
2600 | for (i=0;i<state->common->num_paths;i++) { | ||
2601 | struct path *path = &state->common->paths[i]; | ||
2602 | |||
2603 | if (is_hint_stale(ds, hflash, state, path->grid_start)) { | ||
2604 | draw_path_hint(dr, ds, &state->common->params, path->grid_start, | ||
2605 | hflash, path->sightings_start); | ||
2606 | } | ||
2607 | |||
2608 | if (is_hint_stale(ds, hflash, state, path->grid_end)) { | ||
2609 | draw_path_hint(dr, ds, &state->common->params, path->grid_end, | ||
2610 | hflash, path->sightings_end); | ||
2611 | } | ||
2612 | } | ||
2613 | |||
2614 | /* Draw puzzle grid contents */ | ||
2615 | for (x = 1; x < ds->w+1; x++) | ||
2616 | for (y = 1; y < ds->h+1; y++) { | ||
2617 | stale = FALSE; | ||
2618 | xy = x+y*(state->common->params.w+2); | ||
2619 | xi = state->common->xinfo[xy]; | ||
2620 | c = state->common->grid[xy]; | ||
2621 | |||
2622 | if (!ds->started) stale = TRUE; | ||
2623 | if (ds->hflash != hflash) stale = TRUE; | ||
2624 | if (changed_ascii) stale = TRUE; | ||
2625 | |||
2626 | if (hchanged) { | ||
2627 | if ((x == ui->hx && y == ui->hy) || | ||
2628 | (x == ds->hx && y == ds->hy)) | ||
2629 | stale = TRUE; | ||
2630 | } | ||
2631 | |||
2632 | if (xi >= 0 && (state->guess[xi] != ds->monsters[xi]) ) { | ||
2633 | stale = TRUE; | ||
2634 | ds->monsters[xi] = state->guess[xi]; | ||
2635 | } | ||
2636 | |||
2637 | if (xi >= 0 && (state->pencils[xi] != ds->pencils[xi]) ) { | ||
2638 | stale = TRUE; | ||
2639 | ds->pencils[xi] = state->pencils[xi]; | ||
2640 | } | ||
2641 | |||
2642 | if (state->cell_errors[xy] != ds->cell_errors[xy]) { | ||
2643 | stale = TRUE; | ||
2644 | ds->cell_errors[xy] = state->cell_errors[xy]; | ||
2645 | } | ||
2646 | |||
2647 | if (stale) { | ||
2648 | draw_cell_background(dr, ds, state, ui, x, y); | ||
2649 | if (xi < 0) | ||
2650 | draw_mirror(dr, ds, state, x, y, hflash, c); | ||
2651 | else if (state->guess[xi] == 1 || state->guess[xi] == 2 || | ||
2652 | state->guess[xi] == 4) | ||
2653 | draw_big_monster(dr, ds, state, x, y, hflash, state->guess[xi]); | ||
2654 | else | ||
2655 | draw_pencils(dr, ds, state, x, y, state->pencils[xi]); | ||
2656 | } | ||
2657 | } | ||
2658 | |||
2659 | ds->hx = ui->hx; ds->hy = ui->hy; | ||
2660 | ds->hshow = ui->hshow; | ||
2661 | ds->hpencil = ui->hpencil; | ||
2662 | ds->hflash = hflash; | ||
2663 | ds->started = TRUE; | ||
2664 | return; | ||
2665 | } | ||
2666 | |||
2667 | static float game_anim_length(const game_state *oldstate, | ||
2668 | const game_state *newstate, int dir, game_ui *ui) | ||
2669 | { | ||
2670 | return 0.0F; | ||
2671 | } | ||
2672 | |||
2673 | static float game_flash_length(const game_state *oldstate, | ||
2674 | const game_state *newstate, int dir, game_ui *ui) | ||
2675 | { | ||
2676 | return (!oldstate->solved && newstate->solved && !oldstate->cheated && | ||
2677 | !newstate->cheated) ? FLASH_TIME : 0.0F; | ||
2678 | } | ||
2679 | |||
2680 | static int game_status(const game_state *state) | ||
2681 | { | ||
2682 | return state->solved; | ||
2683 | } | ||
2684 | |||
2685 | static int game_timing_state(const game_state *state, game_ui *ui) | ||
2686 | { | ||
2687 | return TRUE; | ||
2688 | } | ||
2689 | |||
2690 | static void game_print_size(const game_params *params, float *x, float *y) | ||
2691 | { | ||
2692 | } | ||
2693 | |||
2694 | static void game_print(drawing *dr, const game_state *state, int tilesize) | ||
2695 | { | ||
2696 | } | ||
2697 | |||
2698 | #ifdef COMBINED | ||
2699 | #define thegame undead | ||
2700 | #endif | ||
2701 | |||
2702 | const struct game thegame = { | ||
2703 | "Undead", "games.undead", "undead", | ||
2704 | default_params, | ||
2705 | game_fetch_preset, | ||
2706 | decode_params, | ||
2707 | encode_params, | ||
2708 | free_params, | ||
2709 | dup_params, | ||
2710 | TRUE, game_configure, custom_params, | ||
2711 | validate_params, | ||
2712 | new_game_desc, | ||
2713 | validate_desc, | ||
2714 | new_game, | ||
2715 | dup_game, | ||
2716 | free_game, | ||
2717 | TRUE, solve_game, | ||
2718 | TRUE, game_can_format_as_text_now, game_text_format, | ||
2719 | new_ui, | ||
2720 | free_ui, | ||
2721 | encode_ui, | ||
2722 | decode_ui, | ||
2723 | game_changed_state, | ||
2724 | interpret_move, | ||
2725 | execute_move, | ||
2726 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, | ||
2727 | game_colours, | ||
2728 | game_new_drawstate, | ||
2729 | game_free_drawstate, | ||
2730 | game_redraw, | ||
2731 | game_anim_length, | ||
2732 | game_flash_length, | ||
2733 | game_status, | ||
2734 | FALSE, FALSE, game_print_size, game_print, | ||
2735 | FALSE, /* wants_statusbar */ | ||
2736 | FALSE, game_timing_state, | ||
2737 | 0, /* flags */ | ||
2738 | }; | ||