diff options
Diffstat (limited to 'apps/plugins/puzzles/src/unfinished')
-rw-r--r-- | apps/plugins/puzzles/src/unfinished/CMakeLists.txt | 31 | ||||
-rw-r--r-- | apps/plugins/puzzles/src/unfinished/README | 14 | ||||
-rw-r--r-- | apps/plugins/puzzles/src/unfinished/group.c | 2497 | ||||
-rw-r--r-- | apps/plugins/puzzles/src/unfinished/group.gap | 97 | ||||
-rw-r--r-- | apps/plugins/puzzles/src/unfinished/numgame.c | 1294 | ||||
-rw-r--r-- | apps/plugins/puzzles/src/unfinished/path.c | 866 | ||||
-rw-r--r-- | apps/plugins/puzzles/src/unfinished/separate.c | 861 | ||||
-rw-r--r-- | apps/plugins/puzzles/src/unfinished/slide.c | 2444 | ||||
-rw-r--r-- | apps/plugins/puzzles/src/unfinished/sokoban.c | 1476 |
9 files changed, 9580 insertions, 0 deletions
diff --git a/apps/plugins/puzzles/src/unfinished/CMakeLists.txt b/apps/plugins/puzzles/src/unfinished/CMakeLists.txt new file mode 100644 index 0000000000..0c1e331f9b --- /dev/null +++ b/apps/plugins/puzzles/src/unfinished/CMakeLists.txt | |||
@@ -0,0 +1,31 @@ | |||
1 | puzzle(group | ||
2 | DISPLAYNAME "Group" | ||
3 | DESCRIPTION "Group theory puzzle" | ||
4 | OBJECTIVE "Complete the unfinished Cayley table of a group.") | ||
5 | solver(group ${CMAKE_SOURCE_DIR}/latin.c) | ||
6 | |||
7 | puzzle(separate | ||
8 | DISPLAYNAME "Separate" | ||
9 | DESCRIPTION "Rectangle-dividing puzzle" | ||
10 | OBJECTIVE "Partition the grid into regions containing one of each letter.") | ||
11 | |||
12 | puzzle(slide | ||
13 | DISPLAYNAME "Slide" | ||
14 | DESCRIPTION "Sliding block puzzle" | ||
15 | OBJECTIVE "Slide the blocks to let the key block out.") | ||
16 | solver(slide) | ||
17 | |||
18 | puzzle(sokoban | ||
19 | DISPLAYNAME "Sokoban" | ||
20 | DESCRIPTION "Barrel-pushing puzzle" | ||
21 | OBJECTIVE "Push all the barrels into the target squares.") | ||
22 | |||
23 | # These unfinished programs don't even have the structure of a puzzle | ||
24 | # game yet; they're just command-line programs containing test | ||
25 | # implementations of some of the needed functionality. | ||
26 | |||
27 | cliprogram(numgame numgame.c) | ||
28 | |||
29 | cliprogram(path path.c COMPILE_DEFINITIONS TEST_GEN) | ||
30 | |||
31 | export_variables_to_parent_scope() | ||
diff --git a/apps/plugins/puzzles/src/unfinished/README b/apps/plugins/puzzles/src/unfinished/README new file mode 100644 index 0000000000..c96ccc935a --- /dev/null +++ b/apps/plugins/puzzles/src/unfinished/README | |||
@@ -0,0 +1,14 @@ | |||
1 | This subdirectory contains puzzle implementations which are | ||
2 | half-written, fundamentally flawed, or in other ways unready to be | ||
3 | shipped as part of the polished Puzzles collection. | ||
4 | |||
5 | The CMake build system will _build_ all of the source in this | ||
6 | directory (to ensure it hasn't become unbuildable), but they won't be | ||
7 | included in all-in-one puzzle binaries or installed by 'make install' | ||
8 | targets. If you want to temporarily change that, you can reconfigure | ||
9 | your build by defining the CMake variable PUZZLES_ENABLE_UNFINISHED. | ||
10 | For example, | ||
11 | |||
12 | cmake . -DPUZZLES_ENABLE_UNFINISHED="group;slide" | ||
13 | |||
14 | will build as if both Group and Slide were fully official puzzles. | ||
diff --git a/apps/plugins/puzzles/src/unfinished/group.c b/apps/plugins/puzzles/src/unfinished/group.c new file mode 100644 index 0000000000..faffa89485 --- /dev/null +++ b/apps/plugins/puzzles/src/unfinished/group.c | |||
@@ -0,0 +1,2497 @@ | |||
1 | /* | ||
2 | * group.c: a Latin-square puzzle, but played with groups' Cayley | ||
3 | * tables. That is, you are given a Cayley table of a group with | ||
4 | * most elements blank and a few clues, and you must fill it in | ||
5 | * so as to preserve the group axioms. | ||
6 | * | ||
7 | * This is a perfectly playable and fully working puzzle, but I'm | ||
8 | * leaving it for the moment in the 'unfinished' directory because | ||
9 | * it's just too esoteric (not to mention _hard_) for me to be | ||
10 | * comfortable presenting it to the general public as something they | ||
11 | * might (implicitly) actually want to play. | ||
12 | * | ||
13 | * TODO: | ||
14 | * | ||
15 | * - more solver techniques? | ||
16 | * * Inverses: once we know that gh = e, we can immediately | ||
17 | * deduce hg = e as well; then for any gx=y we can deduce | ||
18 | * hy=x, and for any xg=y we have yh=x. | ||
19 | * * Hard-mode associativity: we currently deduce based on | ||
20 | * definite numbers in the grid, but we could also winnow | ||
21 | * based on _possible_ numbers. | ||
22 | * * My overambitious original thoughts included wondering if we | ||
23 | * could infer that there must be elements of certain orders | ||
24 | * (e.g. a group of order divisible by 5 must contain an | ||
25 | * element of order 5), but I think in fact this is probably | ||
26 | * silly. | ||
27 | */ | ||
28 | |||
29 | #include <stdio.h> | ||
30 | #include <stdlib.h> | ||
31 | #include <string.h> | ||
32 | #include <assert.h> | ||
33 | #include <ctype.h> | ||
34 | #ifdef NO_TGMATH_H | ||
35 | # include <math.h> | ||
36 | #else | ||
37 | # include <tgmath.h> | ||
38 | #endif | ||
39 | |||
40 | #include "puzzles.h" | ||
41 | #include "latin.h" | ||
42 | |||
43 | /* | ||
44 | * Difficulty levels. I do some macro ickery here to ensure that my | ||
45 | * enum and the various forms of my name list always match up. | ||
46 | */ | ||
47 | #define DIFFLIST(A) \ | ||
48 | A(TRIVIAL,Trivial,NULL,t) \ | ||
49 | A(NORMAL,Normal,solver_normal,n) \ | ||
50 | A(HARD,Hard,solver_hard,h) \ | ||
51 | A(EXTREME,Extreme,NULL,x) \ | ||
52 | A(UNREASONABLE,Unreasonable,NULL,u) | ||
53 | #define ENUM(upper,title,func,lower) DIFF_ ## upper, | ||
54 | #define TITLE(upper,title,func,lower) #title, | ||
55 | #define ENCODE(upper,title,func,lower) #lower | ||
56 | #define CONFIG(upper,title,func,lower) ":" #title | ||
57 | enum { DIFFLIST(ENUM) DIFFCOUNT }; | ||
58 | static char const *const group_diffnames[] = { DIFFLIST(TITLE) }; | ||
59 | static char const group_diffchars[] = DIFFLIST(ENCODE); | ||
60 | #define DIFFCONFIG DIFFLIST(CONFIG) | ||
61 | |||
62 | enum { | ||
63 | COL_BACKGROUND, | ||
64 | COL_GRID, | ||
65 | COL_USER, | ||
66 | COL_HIGHLIGHT, | ||
67 | COL_ERROR, | ||
68 | COL_PENCIL, | ||
69 | COL_DIAGONAL, | ||
70 | NCOLOURS | ||
71 | }; | ||
72 | |||
73 | /* | ||
74 | * In identity mode, we number the elements e,a,b,c,d,f,g,h,... | ||
75 | * Otherwise, they're a,b,c,d,e,f,g,h,... in the obvious way. | ||
76 | */ | ||
77 | #define E_TO_FRONT(c,id) ( (id) && (c)<=5 ? (c) % 5 + 1 : (c) ) | ||
78 | #define E_FROM_FRONT(c,id) ( (id) && (c)<=5 ? ((c) + 3) % 5 + 1 : (c) ) | ||
79 | |||
80 | #define FROMCHAR(c,id) E_TO_FRONT((((c)-('A'-1)) & ~0x20), id) | ||
81 | #define ISCHAR(c) (((c)>='A'&&(c)<='Z') || ((c)>='a'&&(c)<='z')) | ||
82 | #define TOCHAR(c,id) (E_FROM_FRONT(c,id) + ('a'-1)) | ||
83 | |||
84 | struct game_params { | ||
85 | int w, diff; | ||
86 | bool id; | ||
87 | }; | ||
88 | |||
89 | typedef struct group_common { | ||
90 | int refcount; | ||
91 | bool *immutable; | ||
92 | } group_common; | ||
93 | |||
94 | struct game_state { | ||
95 | game_params par; | ||
96 | digit *grid; | ||
97 | int *pencil; /* bitmaps using bits 1<<1..1<<n */ | ||
98 | group_common *common; | ||
99 | bool completed, cheated; | ||
100 | digit *sequence; /* sequence of group elements shown */ | ||
101 | |||
102 | /* | ||
103 | * This array indicates thick lines separating rows and columns | ||
104 | * placed and unplaced manually by the user as a visual aid, e.g. | ||
105 | * to delineate a subgroup and its cosets. | ||
106 | * | ||
107 | * When a line is placed, it's deemed to be between the two | ||
108 | * particular group elements that are on either side of it at the | ||
109 | * time; dragging those two away from each other automatically | ||
110 | * gets rid of the line. Hence, for a given element i, dividers[i] | ||
111 | * is either -1 (indicating no divider to the right of i), or some | ||
112 | * other element (indicating a divider to the right of i iff that | ||
113 | * element is the one right of it). These are eagerly cleared | ||
114 | * during drags. | ||
115 | */ | ||
116 | int *dividers; /* thick lines between rows/cols */ | ||
117 | }; | ||
118 | |||
119 | static game_params *default_params(void) | ||
120 | { | ||
121 | game_params *ret = snew(game_params); | ||
122 | |||
123 | ret->w = 6; | ||
124 | ret->diff = DIFF_NORMAL; | ||
125 | ret->id = true; | ||
126 | |||
127 | return ret; | ||
128 | } | ||
129 | |||
130 | static const struct game_params group_presets[] = { | ||
131 | { 6, DIFF_NORMAL, true }, | ||
132 | { 6, DIFF_NORMAL, false }, | ||
133 | { 8, DIFF_NORMAL, true }, | ||
134 | { 8, DIFF_NORMAL, false }, | ||
135 | { 8, DIFF_HARD, true }, | ||
136 | { 8, DIFF_HARD, false }, | ||
137 | { 12, DIFF_NORMAL, true }, | ||
138 | }; | ||
139 | |||
140 | static bool game_fetch_preset(int i, char **name, game_params **params) | ||
141 | { | ||
142 | game_params *ret; | ||
143 | char buf[80]; | ||
144 | |||
145 | if (i < 0 || i >= lenof(group_presets)) | ||
146 | return false; | ||
147 | |||
148 | ret = snew(game_params); | ||
149 | *ret = group_presets[i]; /* structure copy */ | ||
150 | |||
151 | sprintf(buf, "%dx%d %s%s", ret->w, ret->w, group_diffnames[ret->diff], | ||
152 | ret->id ? "" : ", identity hidden"); | ||
153 | |||
154 | *name = dupstr(buf); | ||
155 | *params = ret; | ||
156 | return true; | ||
157 | } | ||
158 | |||
159 | static void free_params(game_params *params) | ||
160 | { | ||
161 | sfree(params); | ||
162 | } | ||
163 | |||
164 | static game_params *dup_params(const game_params *params) | ||
165 | { | ||
166 | game_params *ret = snew(game_params); | ||
167 | *ret = *params; /* structure copy */ | ||
168 | return ret; | ||
169 | } | ||
170 | |||
171 | static void decode_params(game_params *params, char const *string) | ||
172 | { | ||
173 | char const *p = string; | ||
174 | |||
175 | params->w = atoi(p); | ||
176 | while (*p && isdigit((unsigned char)*p)) p++; | ||
177 | params->diff = DIFF_NORMAL; | ||
178 | params->id = true; | ||
179 | |||
180 | while (*p) { | ||
181 | if (*p == 'd') { | ||
182 | int i; | ||
183 | p++; | ||
184 | params->diff = DIFFCOUNT+1; /* ...which is invalid */ | ||
185 | if (*p) { | ||
186 | for (i = 0; i < DIFFCOUNT; i++) { | ||
187 | if (*p == group_diffchars[i]) | ||
188 | params->diff = i; | ||
189 | } | ||
190 | p++; | ||
191 | } | ||
192 | } else if (*p == 'i') { | ||
193 | params->id = false; | ||
194 | p++; | ||
195 | } else { | ||
196 | /* unrecognised character */ | ||
197 | p++; | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | |||
202 | static char *encode_params(const game_params *params, bool full) | ||
203 | { | ||
204 | char ret[80]; | ||
205 | |||
206 | sprintf(ret, "%d", params->w); | ||
207 | if (full) | ||
208 | sprintf(ret + strlen(ret), "d%c", group_diffchars[params->diff]); | ||
209 | if (!params->id) | ||
210 | sprintf(ret + strlen(ret), "i"); | ||
211 | |||
212 | return dupstr(ret); | ||
213 | } | ||
214 | |||
215 | static config_item *game_configure(const game_params *params) | ||
216 | { | ||
217 | config_item *ret; | ||
218 | char buf[80]; | ||
219 | |||
220 | ret = snewn(4, config_item); | ||
221 | |||
222 | ret[0].name = "Grid size"; | ||
223 | ret[0].type = C_STRING; | ||
224 | sprintf(buf, "%d", params->w); | ||
225 | ret[0].u.string.sval = dupstr(buf); | ||
226 | |||
227 | ret[1].name = "Difficulty"; | ||
228 | ret[1].type = C_CHOICES; | ||
229 | ret[1].u.choices.choicenames = DIFFCONFIG; | ||
230 | ret[1].u.choices.selected = params->diff; | ||
231 | |||
232 | ret[2].name = "Show identity"; | ||
233 | ret[2].type = C_BOOLEAN; | ||
234 | ret[2].u.boolean.bval = params->id; | ||
235 | |||
236 | ret[3].name = NULL; | ||
237 | ret[3].type = C_END; | ||
238 | |||
239 | return ret; | ||
240 | } | ||
241 | |||
242 | static game_params *custom_params(const config_item *cfg) | ||
243 | { | ||
244 | game_params *ret = snew(game_params); | ||
245 | |||
246 | ret->w = atoi(cfg[0].u.string.sval); | ||
247 | ret->diff = cfg[1].u.choices.selected; | ||
248 | ret->id = cfg[2].u.boolean.bval; | ||
249 | |||
250 | return ret; | ||
251 | } | ||
252 | |||
253 | static const char *validate_params(const game_params *params, bool full) | ||
254 | { | ||
255 | if (params->w < 3 || params->w > 26) | ||
256 | return "Grid size must be between 3 and 26"; | ||
257 | if (params->diff >= DIFFCOUNT) | ||
258 | return "Unknown difficulty rating"; | ||
259 | if (!params->id && params->diff == DIFF_TRIVIAL) { | ||
260 | /* | ||
261 | * We can't have a Trivial-difficulty puzzle (i.e. latin | ||
262 | * square deductions only) without a clear identity, because | ||
263 | * identityless puzzles always have two rows and two columns | ||
264 | * entirely blank, and no latin-square deduction permits the | ||
265 | * distinguishing of two such rows. | ||
266 | */ | ||
267 | return "Trivial puzzles must have an identity"; | ||
268 | } | ||
269 | if (!params->id && params->w == 3) { | ||
270 | /* | ||
271 | * We can't have a 3x3 puzzle without an identity either, | ||
272 | * because 3x3 puzzles can't ever be harder than Trivial | ||
273 | * (there are no 3x3 latin squares which aren't also valid | ||
274 | * group tables, so enabling group-based deductions doesn't | ||
275 | * rule out any possible solutions) and - as above - Trivial | ||
276 | * puzzles can't not have an identity. | ||
277 | */ | ||
278 | return "3x3 puzzles must have an identity"; | ||
279 | } | ||
280 | return NULL; | ||
281 | } | ||
282 | |||
283 | /* ---------------------------------------------------------------------- | ||
284 | * Solver. | ||
285 | */ | ||
286 | |||
287 | static int find_identity(struct latin_solver *solver) | ||
288 | { | ||
289 | int w = solver->o; | ||
290 | digit *grid = solver->grid; | ||
291 | int i, j; | ||
292 | |||
293 | for (i = 0; i < w; i++) | ||
294 | for (j = 0; j < w; j++) { | ||
295 | if (grid[i*w+j] == i+1) | ||
296 | return j+1; | ||
297 | if (grid[i*w+j] == j+1) | ||
298 | return i+1; | ||
299 | } | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | static int solver_normal(struct latin_solver *solver, void *vctx) | ||
305 | { | ||
306 | int w = solver->o; | ||
307 | #ifdef STANDALONE_SOLVER | ||
308 | char **names = solver->names; | ||
309 | #endif | ||
310 | digit *grid = solver->grid; | ||
311 | int i, j, k; | ||
312 | |||
313 | /* | ||
314 | * Deduce using associativity: (ab)c = a(bc). | ||
315 | * | ||
316 | * So we pick any a,b,c we like; then if we know ab, bc, and | ||
317 | * (ab)c we can fill in a(bc). | ||
318 | */ | ||
319 | for (i = 0; i < w; i++) | ||
320 | for (j = 0; j < w; j++) | ||
321 | for (k = 0; k < w; k++) { | ||
322 | if (!grid[i*w+j] || !grid[j*w+k]) | ||
323 | continue; | ||
324 | if (grid[(grid[i*w+j]-1)*w+k] && | ||
325 | !grid[i*w+(grid[j*w+k]-1)]) { | ||
326 | int x = grid[j*w+k]-1, y = i; | ||
327 | int n = grid[(grid[i*w+j]-1)*w+k]; | ||
328 | #ifdef STANDALONE_SOLVER | ||
329 | if (solver_show_working) { | ||
330 | printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n", | ||
331 | solver_recurse_depth*4, "", | ||
332 | names[i], names[j], names[k], | ||
333 | names[grid[i*w+j]-1], names[k], | ||
334 | names[i], names[grid[j*w+k]-1]); | ||
335 | printf("%*s placing %s at (%d,%d)\n", | ||
336 | solver_recurse_depth*4, "", | ||
337 | names[n-1], x+1, y+1); | ||
338 | } | ||
339 | #endif | ||
340 | if (solver->cube[(x*w+y)*w+n-1]) { | ||
341 | latin_solver_place(solver, x, y, n); | ||
342 | return 1; | ||
343 | } else { | ||
344 | #ifdef STANDALONE_SOLVER | ||
345 | if (solver_show_working) | ||
346 | printf("%*s contradiction!\n", | ||
347 | solver_recurse_depth*4, ""); | ||
348 | return -1; | ||
349 | #endif | ||
350 | } | ||
351 | } | ||
352 | if (!grid[(grid[i*w+j]-1)*w+k] && | ||
353 | grid[i*w+(grid[j*w+k]-1)]) { | ||
354 | int x = k, y = grid[i*w+j]-1; | ||
355 | int n = grid[i*w+(grid[j*w+k]-1)]; | ||
356 | #ifdef STANDALONE_SOLVER | ||
357 | if (solver_show_working) { | ||
358 | printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n", | ||
359 | solver_recurse_depth*4, "", | ||
360 | names[i], names[j], names[k], | ||
361 | names[grid[i*w+j]-1], names[k], | ||
362 | names[i], names[grid[j*w+k]-1]); | ||
363 | printf("%*s placing %s at (%d,%d)\n", | ||
364 | solver_recurse_depth*4, "", | ||
365 | names[n-1], x+1, y+1); | ||
366 | } | ||
367 | #endif | ||
368 | if (solver->cube[(x*w+y)*w+n-1]) { | ||
369 | latin_solver_place(solver, x, y, n); | ||
370 | return 1; | ||
371 | } else { | ||
372 | #ifdef STANDALONE_SOLVER | ||
373 | if (solver_show_working) | ||
374 | printf("%*s contradiction!\n", | ||
375 | solver_recurse_depth*4, ""); | ||
376 | return -1; | ||
377 | #endif | ||
378 | } | ||
379 | } | ||
380 | } | ||
381 | |||
382 | /* | ||
383 | * Fill in the row and column for the group identity, if it's not | ||
384 | * already known and if we've just found out what it is. | ||
385 | */ | ||
386 | i = find_identity(solver); | ||
387 | if (i) { | ||
388 | bool done_something = false; | ||
389 | for (j = 1; j <= w; j++) { | ||
390 | if (!grid[(i-1)*w+(j-1)] || !grid[(j-1)*w+(i-1)]) { | ||
391 | done_something = true; | ||
392 | } | ||
393 | } | ||
394 | if (done_something) { | ||
395 | #ifdef STANDALONE_SOLVER | ||
396 | if (solver_show_working) { | ||
397 | printf("%*s%s is the group identity\n", | ||
398 | solver_recurse_depth*4, "", names[i-1]); | ||
399 | } | ||
400 | #endif | ||
401 | for (j = 1; j <= w; j++) { | ||
402 | if (!grid[(j-1)*w+(i-1)]) { | ||
403 | if (!cube(i-1, j-1, j)) { | ||
404 | #ifdef STANDALONE_SOLVER | ||
405 | if (solver_show_working) { | ||
406 | printf("%*s but %s cannot go at (%d,%d) - " | ||
407 | "contradiction!\n", | ||
408 | solver_recurse_depth*4, "", | ||
409 | names[j-1], i, j); | ||
410 | } | ||
411 | #endif | ||
412 | return -1; | ||
413 | } | ||
414 | #ifdef STANDALONE_SOLVER | ||
415 | if (solver_show_working) { | ||
416 | printf("%*s placing %s at (%d,%d)\n", | ||
417 | solver_recurse_depth*4, "", | ||
418 | names[j-1], i, j); | ||
419 | } | ||
420 | #endif | ||
421 | latin_solver_place(solver, i-1, j-1, j); | ||
422 | } | ||
423 | if (!grid[(i-1)*w+(j-1)]) { | ||
424 | if (!cube(j-1, i-1, j)) { | ||
425 | #ifdef STANDALONE_SOLVER | ||
426 | if (solver_show_working) { | ||
427 | printf("%*s but %s cannot go at (%d,%d) - " | ||
428 | "contradiction!\n", | ||
429 | solver_recurse_depth*4, "", | ||
430 | names[j-1], j, i); | ||
431 | } | ||
432 | #endif | ||
433 | return -1; | ||
434 | } | ||
435 | #ifdef STANDALONE_SOLVER | ||
436 | if (solver_show_working) { | ||
437 | printf("%*s placing %s at (%d,%d)\n", | ||
438 | solver_recurse_depth*4, "", | ||
439 | names[j-1], j, i); | ||
440 | } | ||
441 | #endif | ||
442 | latin_solver_place(solver, j-1, i-1, j); | ||
443 | } | ||
444 | } | ||
445 | return 1; | ||
446 | } | ||
447 | } | ||
448 | |||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static int solver_hard(struct latin_solver *solver, void *vctx) | ||
453 | { | ||
454 | bool done_something = false; | ||
455 | int w = solver->o; | ||
456 | #ifdef STANDALONE_SOLVER | ||
457 | char **names = solver->names; | ||
458 | #endif | ||
459 | int i, j; | ||
460 | |||
461 | /* | ||
462 | * In identity-hidden mode, systematically rule out possibilities | ||
463 | * for the group identity. | ||
464 | * | ||
465 | * In solver_normal, we used the fact that any filled square in | ||
466 | * the grid whose contents _does_ match one of the elements it's | ||
467 | * the product of - that is, ab=a or ab=b - tells you immediately | ||
468 | * that the other element is the identity. | ||
469 | * | ||
470 | * Here, we use the flip side of that: any filled square in the | ||
471 | * grid whose contents does _not_ match either its row or column - | ||
472 | * that is, if ab is neither a nor b - tells you immediately that | ||
473 | * _neither_ of those elements is the identity. And if that's | ||
474 | * true, then we can also immediately rule out the possibility | ||
475 | * that it acts as the identity on any element at all. | ||
476 | */ | ||
477 | for (i = 0; i < w; i++) { | ||
478 | bool i_can_be_id = true; | ||
479 | #ifdef STANDALONE_SOLVER | ||
480 | char title[80]; | ||
481 | #endif | ||
482 | |||
483 | for (j = 0; j < w; j++) { | ||
484 | if (grid(i,j) && grid(i,j) != j+1) { | ||
485 | #ifdef STANDALONE_SOLVER | ||
486 | if (solver_show_working) | ||
487 | sprintf(title, "%s cannot be the identity: " | ||
488 | "%s%s = %s =/= %s", names[i], names[i], names[j], | ||
489 | names[grid(i,j)-1], names[j]); | ||
490 | #endif | ||
491 | i_can_be_id = false; | ||
492 | break; | ||
493 | } | ||
494 | if (grid(j,i) && grid(j,i) != j+1) { | ||
495 | #ifdef STANDALONE_SOLVER | ||
496 | if (solver_show_working) | ||
497 | sprintf(title, "%s cannot be the identity: " | ||
498 | "%s%s = %s =/= %s", names[i], names[j], names[i], | ||
499 | names[grid(j,i)-1], names[j]); | ||
500 | #endif | ||
501 | i_can_be_id = false; | ||
502 | break; | ||
503 | } | ||
504 | } | ||
505 | |||
506 | if (!i_can_be_id) { | ||
507 | /* Now rule out ij=j or ji=j for all j. */ | ||
508 | for (j = 0; j < w; j++) { | ||
509 | if (cube(i, j, j+1)) { | ||
510 | #ifdef STANDALONE_SOLVER | ||
511 | if (solver_show_working) { | ||
512 | if (title[0]) { | ||
513 | printf("%*s%s\n", solver_recurse_depth*4, "", | ||
514 | title); | ||
515 | title[0] = '\0'; | ||
516 | } | ||
517 | printf("%*s ruling out %s at (%d,%d)\n", | ||
518 | solver_recurse_depth*4, "", names[j], i, j); | ||
519 | } | ||
520 | #endif | ||
521 | cube(i, j, j+1) = false; | ||
522 | } | ||
523 | if (cube(j, i, j+1)) { | ||
524 | #ifdef STANDALONE_SOLVER | ||
525 | if (solver_show_working) { | ||
526 | if (title[0]) { | ||
527 | printf("%*s%s\n", solver_recurse_depth*4, "", | ||
528 | title); | ||
529 | title[0] = '\0'; | ||
530 | } | ||
531 | printf("%*s ruling out %s at (%d,%d)\n", | ||
532 | solver_recurse_depth*4, "", names[j], j, i); | ||
533 | } | ||
534 | #endif | ||
535 | cube(j, i, j+1) = false; | ||
536 | } | ||
537 | } | ||
538 | } | ||
539 | } | ||
540 | |||
541 | return done_something; | ||
542 | } | ||
543 | |||
544 | #define SOLVER(upper,title,func,lower) func, | ||
545 | static usersolver_t const group_solvers[] = { DIFFLIST(SOLVER) }; | ||
546 | |||
547 | static bool group_valid(struct latin_solver *solver, void *ctx) | ||
548 | { | ||
549 | int w = solver->o; | ||
550 | #ifdef STANDALONE_SOLVER | ||
551 | char **names = solver->names; | ||
552 | #endif | ||
553 | int i, j, k; | ||
554 | |||
555 | for (i = 0; i < w; i++) | ||
556 | for (j = 0; j < w; j++) | ||
557 | for (k = 0; k < w; k++) { | ||
558 | int ij = grid(i, j) - 1; | ||
559 | int jk = grid(j, k) - 1; | ||
560 | int ij_k = grid(ij, k) - 1; | ||
561 | int i_jk = grid(i, jk) - 1; | ||
562 | if (ij_k != i_jk) { | ||
563 | #ifdef STANDALONE_SOLVER | ||
564 | if (solver_show_working) { | ||
565 | printf("%*sfailure of associativity: " | ||
566 | "(%s%s)%s = %s%s = %s but " | ||
567 | "%s(%s%s) = %s%s = %s\n", | ||
568 | solver_recurse_depth*4, "", | ||
569 | names[i], names[j], names[k], | ||
570 | names[ij], names[k], names[ij_k], | ||
571 | names[i], names[j], names[k], | ||
572 | names[i], names[jk], names[i_jk]); | ||
573 | } | ||
574 | #endif | ||
575 | return false; | ||
576 | } | ||
577 | } | ||
578 | |||
579 | return true; | ||
580 | } | ||
581 | |||
582 | static int solver(const game_params *params, digit *grid, int maxdiff) | ||
583 | { | ||
584 | int w = params->w; | ||
585 | int ret; | ||
586 | struct latin_solver solver; | ||
587 | |||
588 | #ifdef STANDALONE_SOLVER | ||
589 | char *p, text[100], *names[50]; | ||
590 | int i; | ||
591 | |||
592 | for (i = 0, p = text; i < w; i++) { | ||
593 | names[i] = p; | ||
594 | *p++ = TOCHAR(i+1, params->id); | ||
595 | *p++ = '\0'; | ||
596 | } | ||
597 | solver.names = names; | ||
598 | #endif | ||
599 | |||
600 | if (latin_solver_alloc(&solver, grid, w)) | ||
601 | ret = latin_solver_main(&solver, maxdiff, | ||
602 | DIFF_TRIVIAL, DIFF_HARD, DIFF_EXTREME, | ||
603 | DIFF_EXTREME, DIFF_UNREASONABLE, | ||
604 | group_solvers, group_valid, NULL, NULL, NULL); | ||
605 | else | ||
606 | ret = diff_impossible; | ||
607 | |||
608 | latin_solver_free(&solver); | ||
609 | |||
610 | return ret; | ||
611 | } | ||
612 | |||
613 | /* ---------------------------------------------------------------------- | ||
614 | * Grid generation. | ||
615 | */ | ||
616 | |||
617 | static char *encode_grid(char *desc, digit *grid, int area) | ||
618 | { | ||
619 | int run, i; | ||
620 | char *p = desc; | ||
621 | |||
622 | run = 0; | ||
623 | for (i = 0; i <= area; i++) { | ||
624 | int n = (i < area ? grid[i] : -1); | ||
625 | |||
626 | if (!n) | ||
627 | run++; | ||
628 | else { | ||
629 | if (run) { | ||
630 | while (run > 0) { | ||
631 | int c = 'a' - 1 + run; | ||
632 | if (run > 26) | ||
633 | c = 'z'; | ||
634 | *p++ = c; | ||
635 | run -= c - ('a' - 1); | ||
636 | } | ||
637 | } else { | ||
638 | /* | ||
639 | * If there's a number in the very top left or | ||
640 | * bottom right, there's no point putting an | ||
641 | * unnecessary _ before or after it. | ||
642 | */ | ||
643 | if (p > desc && n > 0) | ||
644 | *p++ = '_'; | ||
645 | } | ||
646 | if (n > 0) | ||
647 | p += sprintf(p, "%d", n); | ||
648 | run = 0; | ||
649 | } | ||
650 | } | ||
651 | return p; | ||
652 | } | ||
653 | |||
654 | /* ----- data generated by group.gap begins ----- */ | ||
655 | |||
656 | struct group { | ||
657 | unsigned long autosize; | ||
658 | int order, ngens; | ||
659 | const char *gens; | ||
660 | }; | ||
661 | struct groups { | ||
662 | int ngroups; | ||
663 | const struct group *groups; | ||
664 | }; | ||
665 | |||
666 | static const struct group groupdata[] = { | ||
667 | /* order 2 */ | ||
668 | {1L, 2, 1, "BA"}, | ||
669 | /* order 3 */ | ||
670 | {2L, 3, 1, "BCA"}, | ||
671 | /* order 4 */ | ||
672 | {2L, 4, 1, "BCDA"}, | ||
673 | {6L, 4, 2, "BADC" "CDAB"}, | ||
674 | /* order 5 */ | ||
675 | {4L, 5, 1, "BCDEA"}, | ||
676 | /* order 6 */ | ||
677 | {6L, 6, 2, "CFEBAD" "BADCFE"}, | ||
678 | {2L, 6, 1, "DCFEBA"}, | ||
679 | /* order 7 */ | ||
680 | {6L, 7, 1, "BCDEFGA"}, | ||
681 | /* order 8 */ | ||
682 | {4L, 8, 1, "BCEFDGHA"}, | ||
683 | {8L, 8, 2, "BDEFGAHC" "EGBHDCFA"}, | ||
684 | {8L, 8, 2, "EGBHDCFA" "BAEFCDHG"}, | ||
685 | {24L, 8, 2, "BDEFGAHC" "CHDGBEAF"}, | ||
686 | {168L, 8, 3, "BAEFCDHG" "CEAGBHDF" "DFGAHBCE"}, | ||
687 | /* order 9 */ | ||
688 | {6L, 9, 1, "BDECGHFIA"}, | ||
689 | {48L, 9, 2, "BDEAGHCIF" "CEFGHAIBD"}, | ||
690 | /* order 10 */ | ||
691 | {20L, 10, 2, "CJEBGDIFAH" "BADCFEHGJI"}, | ||
692 | {4L, 10, 1, "DCFEHGJIBA"}, | ||
693 | /* order 11 */ | ||
694 | {10L, 11, 1, "BCDEFGHIJKA"}, | ||
695 | /* order 12 */ | ||
696 | {12L, 12, 2, "GLDKJEHCBIAF" "BCEFAGIJDKLH"}, | ||
697 | {4L, 12, 1, "EHIJKCBLDGFA"}, | ||
698 | {24L, 12, 2, "BEFGAIJKCDLH" "FJBKHLEGDCIA"}, | ||
699 | {12L, 12, 2, "GLDKJEHCBIAF" "BAEFCDIJGHLK"}, | ||
700 | {12L, 12, 2, "FDIJGHLBKAEC" "GIDKFLHCJEAB"}, | ||
701 | /* order 13 */ | ||
702 | {12L, 13, 1, "BCDEFGHIJKLMA"}, | ||
703 | /* order 14 */ | ||
704 | {42L, 14, 2, "ELGNIBKDMFAHCJ" "BADCFEHGJILKNM"}, | ||
705 | {6L, 14, 1, "FEHGJILKNMBADC"}, | ||
706 | /* order 15 */ | ||
707 | {8L, 15, 1, "EGHCJKFMNIOBLDA"}, | ||
708 | /* order 16 */ | ||
709 | {8L, 16, 1, "MKNPFOADBGLCIEHJ"}, | ||
710 | {96L, 16, 2, "ILKCONFPEDJHGMAB" "BDFGHIAKLMNCOEPJ"}, | ||
711 | {32L, 16, 2, "MIHPFDCONBLAKJGE" "BEFGHJKALMNOCDPI"}, | ||
712 | {32L, 16, 2, "IFACOGLMDEJBNPKH" "BEFGHJKALMNOCDPI"}, | ||
713 | {16L, 16, 2, "MOHPFKCINBLADJGE" "BDFGHIEKLMNJOAPC"}, | ||
714 | {16L, 16, 2, "MIHPFDJONBLEKCGA" "BDFGHIEKLMNJOAPC"}, | ||
715 | {32L, 16, 2, "MOHPFDCINBLEKJGA" "BAFGHCDELMNIJKPO"}, | ||
716 | {16L, 16, 2, "MIHPFKJONBLADCGE" "GDPHNOEKFLBCIAMJ"}, | ||
717 | {32L, 16, 2, "MIBPFDJOGHLEKCNA" "CLEIJGMPKAOHNFDB"}, | ||
718 | {192L, 16, 3, | ||
719 | "MCHPFAIJNBLDEOGK" "BEFGHJKALMNOCDPI" "GKLBNOEDFPHJIAMC"}, | ||
720 | {64L, 16, 3, "MCHPFAIJNBLDEOGK" "LOGFPKJIBNMEDCHA" "CMAIJHPFDEONBLKG"}, | ||
721 | {192L, 16, 3, | ||
722 | "IPKCOGMLEDJBNFAH" "BEFGHJKALMNOCDPI" "CMEIJBPFKAOGHLDN"}, | ||
723 | {48L, 16, 3, "IPDJONFLEKCBGMAH" "FJBLMEOCGHPKAIND" "DGIEKLHNJOAMPBCF"}, | ||
724 | {20160L, 16, 4, | ||
725 | "EHJKAMNBOCDPFGIL" "BAFGHCDELMNIJKPO" "CFAIJBLMDEOGHPKN" | ||
726 | "DGIAKLBNCOEFPHJM"}, | ||
727 | /* order 17 */ | ||
728 | {16L, 17, 1, "EFGHIJKLMNOPQABCD"}, | ||
729 | /* order 18 */ | ||
730 | {54L, 18, 2, "MKIQOPNAGLRECDBJHF" "BAEFCDJKLGHIOPMNRQ"}, | ||
731 | {6L, 18, 1, "ECJKGHFOPDMNLRIQBA"}, | ||
732 | {12L, 18, 2, "ECJKGHBOPAMNFRDQLI" "KNOPQCFREIGHLJAMBD"}, | ||
733 | {432L, 18, 3, | ||
734 | "IFNAKLQCDOPBGHREMJ" "NOQCFRIGHKLJAMPBDE" "BAEFCDJKLGHIOPMNRQ"}, | ||
735 | {48L, 18, 2, "ECJKGHBOPAMNFRDQLI" "FDKLHIOPBMNAREQCJG"}, | ||
736 | /* order 19 */ | ||
737 | {18L, 19, 1, "EFGHIJKLMNOPQRSABCD"}, | ||
738 | /* order 20 */ | ||
739 | {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "EABICDFMGHJQKLNTOPRS"}, | ||
740 | {8L, 20, 1, "EHIJLCMNPGQRSKBTDOFA"}, | ||
741 | {20L, 20, 2, "DJSHQNCLTRGPEBKAIFOM" "EABICDFMGHJQKLNTOPRS"}, | ||
742 | {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "ECBIAGFMDKJQHONTLSRP"}, | ||
743 | {24L, 20, 2, "IGFMDKJQHONTLSREPCBA" "FDIJGHMNKLQROPTBSAEC"}, | ||
744 | /* order 21 */ | ||
745 | {42L, 21, 2, "ITLSBOUERDHAGKCJNFMQP" "EJHLMKOPNRSQAUTCDBFGI"}, | ||
746 | {12L, 21, 1, "EGHCJKFMNIPQLSTOUBRDA"}, | ||
747 | /* order 22 */ | ||
748 | {110L, 22, 2, "ETGVIBKDMFOHQJSLUNAPCR" "BADCFEHGJILKNMPORQTSVU"}, | ||
749 | {10L, 22, 1, "FEHGJILKNMPORQTSVUBADC"}, | ||
750 | /* order 23 */ | ||
751 | {22L, 23, 1, "EFGHIJKLMNOPQRSTUVWABCD"}, | ||
752 | /* order 24 */ | ||
753 | {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "HRNOPSWCTUVBLDIJXFGAKQME"}, | ||
754 | {8L, 24, 1, "MQBTUDRWFGHXJELINOPKSAVC"}, | ||
755 | {24L, 24, 2, "IOQRBEUVFWGHKLAXMNPSCDTJ" "NJXOVGDKSMTFIPQELCURBWAH"}, | ||
756 | {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "HSNOPWLDTUVBRIAKXFGCQEMJ"}, | ||
757 | {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "TWHNXLRIOPUMSACQVBFDEJGK"}, | ||
758 | {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "BAFGHCDEMNOPIJKLTUVQRSXW"}, | ||
759 | {48L, 24, 3, | ||
760 | "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO" | ||
761 | "HSNOPWLDTUVBRIAKXFGCQEMJ"}, | ||
762 | {24L, 24, 3, | ||
763 | "QUKJWPXFESRIVBMNLDCGHTAO" "JXEQRVUMKLWCPGFTSAIBONDH" | ||
764 | "TRONXLWCHVUMSAIJPGFDEQBK"}, | ||
765 | {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "VKXHOQASNTPBCWDEUFGIJLMR"}, | ||
766 | {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "RMLWIGTUSDJQOPFXEKCBVNAH"}, | ||
767 | {48L, 24, 2, "IULQRGXMSDCWOPNTEKJBVFAH" "GLMOPRSDTUBVWIEKFXHJQANC"}, | ||
768 | {24L, 24, 2, "UJPXMRCSNHGTLWIKFVBEDQOA" "NRUFVLWIPXMOJEDQHGTCSABK"}, | ||
769 | {24L, 24, 2, "MIBTUAQRFGHXCDEWNOPJKLVS" "OKXVFWSCGUTNDRQJBPMALIHE"}, | ||
770 | {144L, 24, 3, | ||
771 | "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO" | ||
772 | "BAFGHCDEMNOPIJKLTUVQRSXW"}, | ||
773 | {336L, 24, 3, | ||
774 | "QTKJWONXESRIHVUMLDCPGFAB" "JNEQRHTUKLWCOPXFSAIVBMDG" | ||
775 | "HENOPJKLTUVBQRSAXFGWCDMI"}, | ||
776 | /* order 25 */ | ||
777 | {20L, 25, 1, "EHILMNPQRSFTUVBJWXDOYGAKC"}, | ||
778 | {480L, 25, 2, "EHILMNPQRSCTUVBFWXDJYGOKA" "BDEGHIKLMNAPQRSCTUVFWXJYO"}, | ||
779 | /* order 26 */ | ||
780 | {156L, 26, 2, | ||
781 | "EXGZIBKDMFOHQJSLUNWPYRATCV" "BADCFEHGJILKNMPORQTSVUXWZY"}, | ||
782 | {12L, 26, 1, "FEHGJILKNMPORQTSVUXWZYBADC"}, | ||
783 | }; | ||
784 | |||
785 | static const struct groups groups[] = { | ||
786 | {0, NULL}, /* trivial case: 0 */ | ||
787 | {0, NULL}, /* trivial case: 1 */ | ||
788 | {1, groupdata + 0}, /* 2 */ | ||
789 | {1, groupdata + 1}, /* 3 */ | ||
790 | {2, groupdata + 2}, /* 4 */ | ||
791 | {1, groupdata + 4}, /* 5 */ | ||
792 | {2, groupdata + 5}, /* 6 */ | ||
793 | {1, groupdata + 7}, /* 7 */ | ||
794 | {5, groupdata + 8}, /* 8 */ | ||
795 | {2, groupdata + 13}, /* 9 */ | ||
796 | {2, groupdata + 15}, /* 10 */ | ||
797 | {1, groupdata + 17}, /* 11 */ | ||
798 | {5, groupdata + 18}, /* 12 */ | ||
799 | {1, groupdata + 23}, /* 13 */ | ||
800 | {2, groupdata + 24}, /* 14 */ | ||
801 | {1, groupdata + 26}, /* 15 */ | ||
802 | {14, groupdata + 27}, /* 16 */ | ||
803 | {1, groupdata + 41}, /* 17 */ | ||
804 | {5, groupdata + 42}, /* 18 */ | ||
805 | {1, groupdata + 47}, /* 19 */ | ||
806 | {5, groupdata + 48}, /* 20 */ | ||
807 | {2, groupdata + 53}, /* 21 */ | ||
808 | {2, groupdata + 55}, /* 22 */ | ||
809 | {1, groupdata + 57}, /* 23 */ | ||
810 | {15, groupdata + 58}, /* 24 */ | ||
811 | {2, groupdata + 73}, /* 25 */ | ||
812 | {2, groupdata + 75}, /* 26 */ | ||
813 | }; | ||
814 | |||
815 | /* ----- data generated by group.gap ends ----- */ | ||
816 | |||
817 | static char *new_game_desc(const game_params *params, random_state *rs, | ||
818 | char **aux, bool interactive) | ||
819 | { | ||
820 | int w = params->w, a = w*w; | ||
821 | digit *grid, *soln, *soln2; | ||
822 | int *indices; | ||
823 | int i, j, k, qh, qt; | ||
824 | int diff = params->diff; | ||
825 | const struct group *group; | ||
826 | char *desc, *p; | ||
827 | |||
828 | /* | ||
829 | * Difficulty exceptions: some combinations of size and | ||
830 | * difficulty cannot be satisfied, because all puzzles of at | ||
831 | * most that difficulty are actually even easier. | ||
832 | * | ||
833 | * Remember to re-test this whenever a change is made to the | ||
834 | * solver logic! | ||
835 | * | ||
836 | * I tested it using the following shell command: | ||
837 | |||
838 | for d in t n h x u; do | ||
839 | for id in '' i; do | ||
840 | for i in {3..9}; do | ||
841 | echo -n "./group --generate 1 ${i}d${d}${id}: " | ||
842 | perl -e 'alarm 30; exec @ARGV' \ | ||
843 | ./group --generate 1 ${i}d${d}${id} >/dev/null && echo ok | ||
844 | done | ||
845 | done | ||
846 | done | ||
847 | |||
848 | * Of course, it's better to do that after taking the exceptions | ||
849 | * _out_, so as to detect exceptions that should be removed as | ||
850 | * well as those which should be added. | ||
851 | */ | ||
852 | if (w < 5 && diff == DIFF_UNREASONABLE) | ||
853 | diff--; | ||
854 | if ((w < 5 || ((w == 6 || w == 8) && params->id)) && diff == DIFF_EXTREME) | ||
855 | diff--; | ||
856 | if ((w < 6 || (w == 6 && params->id)) && diff == DIFF_HARD) | ||
857 | diff--; | ||
858 | if ((w < 4 || (w == 4 && params->id)) && diff == DIFF_NORMAL) | ||
859 | diff--; | ||
860 | |||
861 | grid = snewn(a, digit); | ||
862 | soln = snewn(a, digit); | ||
863 | soln2 = snewn(a, digit); | ||
864 | indices = snewn(a, int); | ||
865 | |||
866 | while (1) { | ||
867 | /* | ||
868 | * Construct a valid group table, by picking a group from | ||
869 | * the above data table, decompressing it into a full | ||
870 | * representation by BFS, and then randomly permuting its | ||
871 | * non-identity elements. | ||
872 | * | ||
873 | * We build the canonical table in 'soln' (and use 'grid' as | ||
874 | * our BFS queue), then transfer the table into 'grid' | ||
875 | * having shuffled the rows. | ||
876 | */ | ||
877 | assert(w >= 2); | ||
878 | assert(w < lenof(groups)); | ||
879 | group = groups[w].groups + random_upto(rs, groups[w].ngroups); | ||
880 | assert(group->order == w); | ||
881 | memset(soln, 0, a); | ||
882 | for (i = 0; i < w; i++) | ||
883 | soln[i] = i+1; | ||
884 | qh = qt = 0; | ||
885 | grid[qt++] = 1; | ||
886 | while (qh < qt) { | ||
887 | digit *row, *newrow; | ||
888 | |||
889 | i = grid[qh++]; | ||
890 | row = soln + (i-1)*w; | ||
891 | |||
892 | for (j = 0; j < group->ngens; j++) { | ||
893 | int nri; | ||
894 | const char *gen = group->gens + j*w; | ||
895 | |||
896 | /* | ||
897 | * Apply each group generator to row, constructing a | ||
898 | * new row. | ||
899 | */ | ||
900 | nri = gen[row[0]-1] - 'A' + 1; /* which row is it? */ | ||
901 | newrow = soln + (nri-1)*w; | ||
902 | if (!newrow[0]) { /* not done yet */ | ||
903 | for (k = 0; k < w; k++) | ||
904 | newrow[k] = gen[row[k]-1] - 'A' + 1; | ||
905 | grid[qt++] = nri; | ||
906 | } | ||
907 | } | ||
908 | } | ||
909 | /* That's got the canonical table. Now shuffle it. */ | ||
910 | for (i = 0; i < w; i++) | ||
911 | soln2[i] = i; | ||
912 | if (params->id) /* do we shuffle in the identity? */ | ||
913 | shuffle(soln2+1, w-1, sizeof(*soln2), rs); | ||
914 | else | ||
915 | shuffle(soln2, w, sizeof(*soln2), rs); | ||
916 | for (i = 0; i < w; i++) | ||
917 | for (j = 0; j < w; j++) | ||
918 | grid[(soln2[i])*w+(soln2[j])] = soln2[soln[i*w+j]-1]+1; | ||
919 | |||
920 | /* | ||
921 | * Remove entries one by one while the puzzle is still | ||
922 | * soluble at the appropriate difficulty level. | ||
923 | */ | ||
924 | memcpy(soln, grid, a); | ||
925 | if (!params->id) { | ||
926 | /* | ||
927 | * Start by blanking the entire identity row and column, | ||
928 | * and also another row and column so that the player | ||
929 | * can't trivially determine which element is the | ||
930 | * identity. | ||
931 | */ | ||
932 | |||
933 | j = 1 + random_upto(rs, w-1); /* pick a second row/col to blank */ | ||
934 | for (i = 0; i < w; i++) { | ||
935 | grid[(soln2[0])*w+i] = grid[i*w+(soln2[0])] = 0; | ||
936 | grid[(soln2[j])*w+i] = grid[i*w+(soln2[j])] = 0; | ||
937 | } | ||
938 | |||
939 | memcpy(soln2, grid, a); | ||
940 | if (solver(params, soln2, diff) > diff) | ||
941 | continue; /* go round again if that didn't work */ | ||
942 | } | ||
943 | |||
944 | k = 0; | ||
945 | for (i = (params->id ? 1 : 0); i < w; i++) | ||
946 | for (j = (params->id ? 1 : 0); j < w; j++) | ||
947 | if (grid[i*w+j]) | ||
948 | indices[k++] = i*w+j; | ||
949 | shuffle(indices, k, sizeof(*indices), rs); | ||
950 | |||
951 | for (i = 0; i < k; i++) { | ||
952 | memcpy(soln2, grid, a); | ||
953 | soln2[indices[i]] = 0; | ||
954 | if (solver(params, soln2, diff) <= diff) | ||
955 | grid[indices[i]] = 0; | ||
956 | } | ||
957 | |||
958 | /* | ||
959 | * Make sure the puzzle isn't too easy. | ||
960 | */ | ||
961 | if (diff > 0) { | ||
962 | memcpy(soln2, grid, a); | ||
963 | if (solver(params, soln2, diff-1) < diff) | ||
964 | continue; /* go round and try again */ | ||
965 | } | ||
966 | |||
967 | /* | ||
968 | * Done. | ||
969 | */ | ||
970 | break; | ||
971 | } | ||
972 | |||
973 | /* | ||
974 | * Encode the puzzle description. | ||
975 | */ | ||
976 | desc = snewn(a*20, char); | ||
977 | p = encode_grid(desc, grid, a); | ||
978 | *p++ = '\0'; | ||
979 | desc = sresize(desc, p - desc, char); | ||
980 | |||
981 | /* | ||
982 | * Encode the solution. | ||
983 | */ | ||
984 | *aux = snewn(a+2, char); | ||
985 | (*aux)[0] = 'S'; | ||
986 | for (i = 0; i < a; i++) | ||
987 | (*aux)[i+1] = TOCHAR(soln[i], params->id); | ||
988 | (*aux)[a+1] = '\0'; | ||
989 | |||
990 | sfree(grid); | ||
991 | sfree(soln); | ||
992 | sfree(soln2); | ||
993 | sfree(indices); | ||
994 | |||
995 | return desc; | ||
996 | } | ||
997 | |||
998 | /* ---------------------------------------------------------------------- | ||
999 | * Gameplay. | ||
1000 | */ | ||
1001 | |||
1002 | static const char *validate_grid_desc(const char **pdesc, int range, int area) | ||
1003 | { | ||
1004 | const char *desc = *pdesc; | ||
1005 | int squares = 0; | ||
1006 | while (*desc && *desc != ',') { | ||
1007 | int n = *desc++; | ||
1008 | if (n >= 'a' && n <= 'z') { | ||
1009 | squares += n - 'a' + 1; | ||
1010 | } else if (n == '_') { | ||
1011 | /* do nothing */; | ||
1012 | } else if (n > '0' && n <= '9') { | ||
1013 | int val = atoi(desc-1); | ||
1014 | if (val < 1 || val > range) | ||
1015 | return "Out-of-range number in game description"; | ||
1016 | squares++; | ||
1017 | while (*desc >= '0' && *desc <= '9') | ||
1018 | desc++; | ||
1019 | } else | ||
1020 | return "Invalid character in game description"; | ||
1021 | } | ||
1022 | |||
1023 | if (squares < area) | ||
1024 | return "Not enough data to fill grid"; | ||
1025 | |||
1026 | if (squares > area) | ||
1027 | return "Too much data to fit in grid"; | ||
1028 | *pdesc = desc; | ||
1029 | return NULL; | ||
1030 | } | ||
1031 | |||
1032 | static const char *validate_desc(const game_params *params, const char *desc) | ||
1033 | { | ||
1034 | int w = params->w, a = w*w; | ||
1035 | const char *p = desc; | ||
1036 | |||
1037 | return validate_grid_desc(&p, w, a); | ||
1038 | } | ||
1039 | |||
1040 | static const char *spec_to_grid(const char *desc, digit *grid, int area) | ||
1041 | { | ||
1042 | int i = 0; | ||
1043 | while (*desc && *desc != ',') { | ||
1044 | int n = *desc++; | ||
1045 | if (n >= 'a' && n <= 'z') { | ||
1046 | int run = n - 'a' + 1; | ||
1047 | assert(i + run <= area); | ||
1048 | while (run-- > 0) | ||
1049 | grid[i++] = 0; | ||
1050 | } else if (n == '_') { | ||
1051 | /* do nothing */; | ||
1052 | } else if (n > '0' && n <= '9') { | ||
1053 | assert(i < area); | ||
1054 | grid[i++] = atoi(desc-1); | ||
1055 | while (*desc >= '0' && *desc <= '9') | ||
1056 | desc++; | ||
1057 | } else { | ||
1058 | assert(!"We can't get here"); | ||
1059 | } | ||
1060 | } | ||
1061 | assert(i == area); | ||
1062 | return desc; | ||
1063 | } | ||
1064 | |||
1065 | static game_state *new_game(midend *me, const game_params *params, | ||
1066 | const char *desc) | ||
1067 | { | ||
1068 | int w = params->w, a = w*w; | ||
1069 | game_state *state = snew(game_state); | ||
1070 | int i; | ||
1071 | |||
1072 | state->par = *params; /* structure copy */ | ||
1073 | state->grid = snewn(a, digit); | ||
1074 | state->common = snew(group_common); | ||
1075 | state->common->refcount = 1; | ||
1076 | state->common->immutable = snewn(a, bool); | ||
1077 | state->pencil = snewn(a, int); | ||
1078 | for (i = 0; i < a; i++) { | ||
1079 | state->grid[i] = 0; | ||
1080 | state->common->immutable[i] = false; | ||
1081 | state->pencil[i] = 0; | ||
1082 | } | ||
1083 | state->sequence = snewn(w, digit); | ||
1084 | state->dividers = snewn(w, int); | ||
1085 | for (i = 0; i < w; i++) { | ||
1086 | state->sequence[i] = i; | ||
1087 | state->dividers[i] = -1; | ||
1088 | } | ||
1089 | |||
1090 | desc = spec_to_grid(desc, state->grid, a); | ||
1091 | for (i = 0; i < a; i++) | ||
1092 | if (state->grid[i] != 0) | ||
1093 | state->common->immutable[i] = true; | ||
1094 | |||
1095 | state->completed = false; | ||
1096 | state->cheated = false; | ||
1097 | |||
1098 | return state; | ||
1099 | } | ||
1100 | |||
1101 | static game_state *dup_game(const game_state *state) | ||
1102 | { | ||
1103 | int w = state->par.w, a = w*w; | ||
1104 | game_state *ret = snew(game_state); | ||
1105 | |||
1106 | ret->par = state->par; /* structure copy */ | ||
1107 | |||
1108 | ret->grid = snewn(a, digit); | ||
1109 | ret->common = state->common; | ||
1110 | ret->common->refcount++; | ||
1111 | ret->pencil = snewn(a, int); | ||
1112 | ret->sequence = snewn(w, digit); | ||
1113 | ret->dividers = snewn(w, int); | ||
1114 | memcpy(ret->grid, state->grid, a*sizeof(digit)); | ||
1115 | memcpy(ret->pencil, state->pencil, a*sizeof(int)); | ||
1116 | memcpy(ret->sequence, state->sequence, w*sizeof(digit)); | ||
1117 | memcpy(ret->dividers, state->dividers, w*sizeof(int)); | ||
1118 | |||
1119 | ret->completed = state->completed; | ||
1120 | ret->cheated = state->cheated; | ||
1121 | |||
1122 | return ret; | ||
1123 | } | ||
1124 | |||
1125 | static void free_game(game_state *state) | ||
1126 | { | ||
1127 | sfree(state->grid); | ||
1128 | if (--state->common->refcount == 0) { | ||
1129 | sfree(state->common->immutable); | ||
1130 | sfree(state->common); | ||
1131 | } | ||
1132 | sfree(state->pencil); | ||
1133 | sfree(state->sequence); | ||
1134 | sfree(state); | ||
1135 | } | ||
1136 | |||
1137 | static char *solve_game(const game_state *state, const game_state *currstate, | ||
1138 | const char *aux, const char **error) | ||
1139 | { | ||
1140 | int w = state->par.w, a = w*w; | ||
1141 | int i, ret; | ||
1142 | digit *soln; | ||
1143 | char *out; | ||
1144 | |||
1145 | if (aux) | ||
1146 | return dupstr(aux); | ||
1147 | |||
1148 | soln = snewn(a, digit); | ||
1149 | memcpy(soln, state->grid, a*sizeof(digit)); | ||
1150 | |||
1151 | ret = solver(&state->par, soln, DIFFCOUNT-1); | ||
1152 | |||
1153 | if (ret == diff_impossible) { | ||
1154 | *error = "No solution exists for this puzzle"; | ||
1155 | out = NULL; | ||
1156 | } else if (ret == diff_ambiguous) { | ||
1157 | *error = "Multiple solutions exist for this puzzle"; | ||
1158 | out = NULL; | ||
1159 | } else { | ||
1160 | out = snewn(a+2, char); | ||
1161 | out[0] = 'S'; | ||
1162 | for (i = 0; i < a; i++) | ||
1163 | out[i+1] = TOCHAR(soln[i], state->par.id); | ||
1164 | out[a+1] = '\0'; | ||
1165 | } | ||
1166 | |||
1167 | sfree(soln); | ||
1168 | return out; | ||
1169 | } | ||
1170 | |||
1171 | static bool game_can_format_as_text_now(const game_params *params) | ||
1172 | { | ||
1173 | return true; | ||
1174 | } | ||
1175 | |||
1176 | static char *game_text_format(const game_state *state) | ||
1177 | { | ||
1178 | int w = state->par.w; | ||
1179 | int x, y; | ||
1180 | char *ret, *p, ch; | ||
1181 | |||
1182 | ret = snewn(2*w*w+1, char); /* leave room for terminating NUL */ | ||
1183 | |||
1184 | p = ret; | ||
1185 | for (y = 0; y < w; y++) { | ||
1186 | for (x = 0; x < w; x++) { | ||
1187 | digit d = state->grid[y*w+x]; | ||
1188 | |||
1189 | if (d == 0) { | ||
1190 | ch = '.'; | ||
1191 | } else { | ||
1192 | ch = TOCHAR(d, state->par.id); | ||
1193 | } | ||
1194 | |||
1195 | *p++ = ch; | ||
1196 | if (x == w-1) { | ||
1197 | *p++ = '\n'; | ||
1198 | } else { | ||
1199 | *p++ = ' '; | ||
1200 | } | ||
1201 | } | ||
1202 | } | ||
1203 | |||
1204 | assert(p - ret == 2*w*w); | ||
1205 | *p = '\0'; | ||
1206 | return ret; | ||
1207 | } | ||
1208 | |||
1209 | struct game_ui { | ||
1210 | /* | ||
1211 | * These are the coordinates of the primary highlighted square on | ||
1212 | * the grid, if hshow = 1. | ||
1213 | */ | ||
1214 | int hx, hy; | ||
1215 | /* | ||
1216 | * These are the coordinates hx,hy _before_ they go through | ||
1217 | * state->sequence. | ||
1218 | */ | ||
1219 | int ohx, ohy; | ||
1220 | /* | ||
1221 | * These variables give the length and displacement of a diagonal | ||
1222 | * sequence of highlighted squares starting at ohx,ohy (still if | ||
1223 | * hshow = 1). To find the squares' real coordinates, for 0<=i<dn, | ||
1224 | * compute ohx+i*odx and ohy+i*ody and then map through | ||
1225 | * state->sequence. | ||
1226 | */ | ||
1227 | int odx, ody, odn; | ||
1228 | /* | ||
1229 | * This indicates whether the current highlight is a | ||
1230 | * pencil-mark one or a real one. | ||
1231 | */ | ||
1232 | bool hpencil; | ||
1233 | /* | ||
1234 | * This indicates whether or not we're showing the highlight | ||
1235 | * (used to be hx = hy = -1); important so that when we're | ||
1236 | * using the cursor keys it doesn't keep coming back at a | ||
1237 | * fixed position. When hshow = 1, pressing a valid number | ||
1238 | * or letter key or Space will enter that number or letter in the grid. | ||
1239 | */ | ||
1240 | bool hshow; | ||
1241 | /* | ||
1242 | * This indicates whether we're using the highlight as a cursor; | ||
1243 | * it means that it doesn't vanish on a keypress, and that it is | ||
1244 | * allowed on immutable squares. | ||
1245 | */ | ||
1246 | bool hcursor; | ||
1247 | /* | ||
1248 | * This indicates whether we're dragging a table header to | ||
1249 | * reposition an entire row or column. | ||
1250 | */ | ||
1251 | int drag; /* 0=none 1=row 2=col */ | ||
1252 | int dragnum; /* element being dragged */ | ||
1253 | int dragpos; /* its current position */ | ||
1254 | int edgepos; | ||
1255 | |||
1256 | /* | ||
1257 | * User preference option: if the user right-clicks in a square | ||
1258 | * and presses a letter key to add/remove a pencil mark, do we | ||
1259 | * hide the mouse highlight again afterwards? | ||
1260 | * | ||
1261 | * Historically our answer was yes. The Android port prefers no. | ||
1262 | * There are advantages both ways, depending how much you dislike | ||
1263 | * the highlight cluttering your view. So it's a preference. | ||
1264 | */ | ||
1265 | bool pencil_keep_highlight; | ||
1266 | }; | ||
1267 | |||
1268 | static game_ui *new_ui(const game_state *state) | ||
1269 | { | ||
1270 | game_ui *ui = snew(game_ui); | ||
1271 | |||
1272 | ui->hx = ui->hy = 0; | ||
1273 | ui->hpencil = false; | ||
1274 | ui->hshow = false; | ||
1275 | ui->hcursor = false; | ||
1276 | ui->drag = 0; | ||
1277 | |||
1278 | ui->pencil_keep_highlight = false; | ||
1279 | |||
1280 | return ui; | ||
1281 | } | ||
1282 | |||
1283 | static void free_ui(game_ui *ui) | ||
1284 | { | ||
1285 | sfree(ui); | ||
1286 | } | ||
1287 | |||
1288 | static config_item *get_prefs(game_ui *ui) | ||
1289 | { | ||
1290 | config_item *ret; | ||
1291 | |||
1292 | ret = snewn(2, config_item); | ||
1293 | |||
1294 | ret[0].name = "Keep mouse highlight after changing a pencil mark"; | ||
1295 | ret[0].kw = "pencil-keep-highlight"; | ||
1296 | ret[0].type = C_BOOLEAN; | ||
1297 | ret[0].u.boolean.bval = ui->pencil_keep_highlight; | ||
1298 | |||
1299 | ret[1].name = NULL; | ||
1300 | ret[1].type = C_END; | ||
1301 | |||
1302 | return ret; | ||
1303 | } | ||
1304 | |||
1305 | static void set_prefs(game_ui *ui, const config_item *cfg) | ||
1306 | { | ||
1307 | ui->pencil_keep_highlight = cfg[0].u.boolean.bval; | ||
1308 | } | ||
1309 | |||
1310 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | ||
1311 | const game_state *newstate) | ||
1312 | { | ||
1313 | int w = newstate->par.w; | ||
1314 | /* | ||
1315 | * We prevent pencil-mode highlighting of a filled square, unless | ||
1316 | * we're using the cursor keys. So if the user has just filled in | ||
1317 | * a square which we had a pencil-mode highlight in (by Undo, or | ||
1318 | * by Redo, or by Solve), then we cancel the highlight. | ||
1319 | */ | ||
1320 | if (ui->hshow && ui->hpencil && !ui->hcursor && | ||
1321 | newstate->grid[ui->hy * w + ui->hx] != 0) { | ||
1322 | ui->hshow = false; | ||
1323 | } | ||
1324 | if (ui->hshow && ui->odn > 1) { | ||
1325 | /* | ||
1326 | * Reordering of rows or columns within the range of a | ||
1327 | * multifill selection cancels the multifill and deselects | ||
1328 | * everything. | ||
1329 | */ | ||
1330 | int i; | ||
1331 | for (i = 0; i < ui->odn; i++) { | ||
1332 | if (oldstate->sequence[ui->ohx + i*ui->odx] != | ||
1333 | newstate->sequence[ui->ohx + i*ui->odx]) { | ||
1334 | ui->hshow = false; | ||
1335 | break; | ||
1336 | } | ||
1337 | if (oldstate->sequence[ui->ohy + i*ui->ody] != | ||
1338 | newstate->sequence[ui->ohy + i*ui->ody]) { | ||
1339 | ui->hshow = false; | ||
1340 | break; | ||
1341 | } | ||
1342 | } | ||
1343 | } else if (ui->hshow && | ||
1344 | (newstate->sequence[ui->ohx] != ui->hx || | ||
1345 | newstate->sequence[ui->ohy] != ui->hy)) { | ||
1346 | /* | ||
1347 | * Otherwise, reordering of the row or column containing the | ||
1348 | * selection causes the selection to move with it. | ||
1349 | */ | ||
1350 | int i; | ||
1351 | for (i = 0; i < w; i++) { | ||
1352 | if (newstate->sequence[i] == ui->hx) | ||
1353 | ui->ohx = i; | ||
1354 | if (newstate->sequence[i] == ui->hy) | ||
1355 | ui->ohy = i; | ||
1356 | } | ||
1357 | } | ||
1358 | } | ||
1359 | |||
1360 | static const char *current_key_label(const game_ui *ui, | ||
1361 | const game_state *state, int button) | ||
1362 | { | ||
1363 | if (ui->hshow && button == CURSOR_SELECT) | ||
1364 | return ui->hpencil ? "Ink" : "Pencil"; | ||
1365 | if (ui->hshow && button == CURSOR_SELECT2) { | ||
1366 | int w = state->par.w; | ||
1367 | int i; | ||
1368 | for (i = 0; i < ui->odn; i++) { | ||
1369 | int x = state->sequence[ui->ohx + i*ui->odx]; | ||
1370 | int y = state->sequence[ui->ohy + i*ui->ody]; | ||
1371 | int index = y*w+x; | ||
1372 | if (ui->hpencil && state->grid[index]) return ""; | ||
1373 | if (state->common->immutable[index]) return ""; | ||
1374 | } | ||
1375 | return "Clear"; | ||
1376 | } | ||
1377 | return ""; | ||
1378 | } | ||
1379 | |||
1380 | #define PREFERRED_TILESIZE 48 | ||
1381 | #define TILESIZE (ds->tilesize) | ||
1382 | #define BORDER (TILESIZE / 2) | ||
1383 | #define LEGEND (TILESIZE) | ||
1384 | #define GRIDEXTRA max((TILESIZE / 32),1) | ||
1385 | #define COORD(x) ((x)*TILESIZE + BORDER + LEGEND) | ||
1386 | #define FROMCOORD(x) (((x)+(TILESIZE-BORDER-LEGEND)) / TILESIZE - 1) | ||
1387 | |||
1388 | #define FLASH_TIME 0.4F | ||
1389 | |||
1390 | #define DF_DIVIDER_TOP 0x1000 | ||
1391 | #define DF_DIVIDER_BOT 0x2000 | ||
1392 | #define DF_DIVIDER_LEFT 0x4000 | ||
1393 | #define DF_DIVIDER_RIGHT 0x8000 | ||
1394 | #define DF_HIGHLIGHT 0x0400 | ||
1395 | #define DF_HIGHLIGHT_PENCIL 0x0200 | ||
1396 | #define DF_IMMUTABLE 0x0100 | ||
1397 | #define DF_LEGEND 0x0080 | ||
1398 | #define DF_DIGIT_MASK 0x001F | ||
1399 | |||
1400 | #define EF_DIGIT_SHIFT 5 | ||
1401 | #define EF_DIGIT_MASK ((1 << EF_DIGIT_SHIFT) - 1) | ||
1402 | #define EF_LEFT_SHIFT 0 | ||
1403 | #define EF_RIGHT_SHIFT (3*EF_DIGIT_SHIFT) | ||
1404 | #define EF_LEFT_MASK ((1UL << (3*EF_DIGIT_SHIFT)) - 1UL) | ||
1405 | #define EF_RIGHT_MASK (EF_LEFT_MASK << EF_RIGHT_SHIFT) | ||
1406 | #define EF_LATIN (1UL << (6*EF_DIGIT_SHIFT)) | ||
1407 | |||
1408 | struct game_drawstate { | ||
1409 | game_params par; | ||
1410 | int w, tilesize; | ||
1411 | bool started; | ||
1412 | long *tiles, *legend, *pencil, *errors; | ||
1413 | long *errtmp; | ||
1414 | digit *sequence; | ||
1415 | }; | ||
1416 | |||
1417 | static bool check_errors(const game_state *state, long *errors) | ||
1418 | { | ||
1419 | int w = state->par.w, a = w*w; | ||
1420 | digit *grid = state->grid; | ||
1421 | int i, j, k, x, y; | ||
1422 | bool errs = false; | ||
1423 | |||
1424 | /* | ||
1425 | * To verify that we have a valid group table, it suffices to | ||
1426 | * test latin-square-hood and associativity only. All the other | ||
1427 | * group axioms follow from those two. | ||
1428 | * | ||
1429 | * Proof: | ||
1430 | * | ||
1431 | * Associativity is given; closure is obvious from latin- | ||
1432 | * square-hood. We need to show that an identity exists and that | ||
1433 | * every element has an inverse. | ||
1434 | * | ||
1435 | * Identity: take any element a. There will be some element e | ||
1436 | * such that ea=a (in a latin square, every element occurs in | ||
1437 | * every row and column, so a must occur somewhere in the a | ||
1438 | * column, say on row e). For any other element b, there must | ||
1439 | * exist x such that ax=b (same argument from latin-square-hood | ||
1440 | * again), and then associativity gives us eb = e(ax) = (ea)x = | ||
1441 | * ax = b. Hence eb=b for all b, i.e. e is a left-identity. A | ||
1442 | * similar argument tells us that there must be some f which is | ||
1443 | * a right-identity, and then we show they are the same element | ||
1444 | * by observing that ef must simultaneously equal e and equal f. | ||
1445 | * | ||
1446 | * Inverses: given any a, by the latin-square argument again, | ||
1447 | * there must exist p and q such that pa=e and aq=e (i.e. left- | ||
1448 | * and right-inverses). We can show these are equal by | ||
1449 | * associativity: p = pe = p(aq) = (pa)q = eq = q. [] | ||
1450 | */ | ||
1451 | |||
1452 | if (errors) | ||
1453 | for (i = 0; i < a; i++) | ||
1454 | errors[i] = 0; | ||
1455 | |||
1456 | for (y = 0; y < w; y++) { | ||
1457 | unsigned long mask = 0, errmask = 0; | ||
1458 | for (x = 0; x < w; x++) { | ||
1459 | unsigned long bit = 1UL << grid[y*w+x]; | ||
1460 | errmask |= (mask & bit); | ||
1461 | mask |= bit; | ||
1462 | } | ||
1463 | |||
1464 | if (mask != (1 << (w+1)) - (1 << 1)) { | ||
1465 | errs = true; | ||
1466 | errmask &= ~1UL; | ||
1467 | if (errors) { | ||
1468 | for (x = 0; x < w; x++) | ||
1469 | if (errmask & (1UL << grid[y*w+x])) | ||
1470 | errors[y*w+x] |= EF_LATIN; | ||
1471 | } | ||
1472 | } | ||
1473 | } | ||
1474 | |||
1475 | for (x = 0; x < w; x++) { | ||
1476 | unsigned long mask = 0, errmask = 0; | ||
1477 | for (y = 0; y < w; y++) { | ||
1478 | unsigned long bit = 1UL << grid[y*w+x]; | ||
1479 | errmask |= (mask & bit); | ||
1480 | mask |= bit; | ||
1481 | } | ||
1482 | |||
1483 | if (mask != (1 << (w+1)) - (1 << 1)) { | ||
1484 | errs = true; | ||
1485 | errmask &= ~1UL; | ||
1486 | if (errors) { | ||
1487 | for (y = 0; y < w; y++) | ||
1488 | if (errmask & (1UL << grid[y*w+x])) | ||
1489 | errors[y*w+x] |= EF_LATIN; | ||
1490 | } | ||
1491 | } | ||
1492 | } | ||
1493 | |||
1494 | for (i = 1; i < w; i++) | ||
1495 | for (j = 1; j < w; j++) | ||
1496 | for (k = 1; k < w; k++) | ||
1497 | if (grid[i*w+j] && grid[j*w+k] && | ||
1498 | grid[(grid[i*w+j]-1)*w+k] && | ||
1499 | grid[i*w+(grid[j*w+k]-1)] && | ||
1500 | grid[(grid[i*w+j]-1)*w+k] != grid[i*w+(grid[j*w+k]-1)]) { | ||
1501 | if (errors) { | ||
1502 | int a = i+1, b = j+1, c = k+1; | ||
1503 | int ab = grid[i*w+j], bc = grid[j*w+k]; | ||
1504 | int left = (ab-1)*w+(c-1), right = (a-1)*w+(bc-1); | ||
1505 | /* | ||
1506 | * If the appropriate error slot is already | ||
1507 | * used for one of the squares, we don't | ||
1508 | * fill either of them. | ||
1509 | */ | ||
1510 | if (!(errors[left] & EF_LEFT_MASK) && | ||
1511 | !(errors[right] & EF_RIGHT_MASK)) { | ||
1512 | long err; | ||
1513 | err = a; | ||
1514 | err = (err << EF_DIGIT_SHIFT) | b; | ||
1515 | err = (err << EF_DIGIT_SHIFT) | c; | ||
1516 | errors[left] |= err << EF_LEFT_SHIFT; | ||
1517 | errors[right] |= err << EF_RIGHT_SHIFT; | ||
1518 | } | ||
1519 | } | ||
1520 | errs = true; | ||
1521 | } | ||
1522 | |||
1523 | return errs; | ||
1524 | } | ||
1525 | |||
1526 | static int find_in_sequence(digit *seq, int len, digit n) | ||
1527 | { | ||
1528 | int i; | ||
1529 | |||
1530 | for (i = 0; i < len; i++) | ||
1531 | if (seq[i] == n) | ||
1532 | return i; | ||
1533 | |||
1534 | assert(!"Should never get here"); | ||
1535 | return -1; | ||
1536 | } | ||
1537 | |||
1538 | static char *interpret_move(const game_state *state, game_ui *ui, | ||
1539 | const game_drawstate *ds, | ||
1540 | int x, int y, int button) | ||
1541 | { | ||
1542 | int w = state->par.w; | ||
1543 | int tx, ty; | ||
1544 | char buf[80]; | ||
1545 | |||
1546 | button = STRIP_BUTTON_MODIFIERS(button); | ||
1547 | |||
1548 | tx = FROMCOORD(x); | ||
1549 | ty = FROMCOORD(y); | ||
1550 | |||
1551 | if (ui->drag) { | ||
1552 | if (IS_MOUSE_DRAG(button)) { | ||
1553 | int tcoord = ((ui->drag &~ 4) == 1 ? ty : tx); | ||
1554 | ui->drag |= 4; /* some movement has happened */ | ||
1555 | if (tcoord >= 0 && tcoord < w) { | ||
1556 | ui->dragpos = tcoord; | ||
1557 | return MOVE_UI_UPDATE; | ||
1558 | } | ||
1559 | } else if (IS_MOUSE_RELEASE(button)) { | ||
1560 | if (ui->drag & 4) { | ||
1561 | ui->drag = 0; /* end drag */ | ||
1562 | if (state->sequence[ui->dragpos] == ui->dragnum) | ||
1563 | return MOVE_UI_UPDATE; /* drag was a no-op overall */ | ||
1564 | sprintf(buf, "D%d,%d", ui->dragnum, ui->dragpos); | ||
1565 | return dupstr(buf); | ||
1566 | } else { | ||
1567 | ui->drag = 0; /* end 'drag' */ | ||
1568 | if (ui->edgepos > 0 && ui->edgepos < w) { | ||
1569 | sprintf(buf, "V%d,%d", | ||
1570 | state->sequence[ui->edgepos-1], | ||
1571 | state->sequence[ui->edgepos]); | ||
1572 | return dupstr(buf); | ||
1573 | } else | ||
1574 | return MOVE_UI_UPDATE; /* no-op */ | ||
1575 | } | ||
1576 | } | ||
1577 | } else if (IS_MOUSE_DOWN(button)) { | ||
1578 | if (tx >= 0 && tx < w && ty >= 0 && ty < w) { | ||
1579 | int otx = tx, oty = ty; | ||
1580 | tx = state->sequence[tx]; | ||
1581 | ty = state->sequence[ty]; | ||
1582 | if (button == LEFT_BUTTON) { | ||
1583 | if (tx == ui->hx && ty == ui->hy && | ||
1584 | ui->hshow && !ui->hpencil) { | ||
1585 | ui->hshow = false; | ||
1586 | } else { | ||
1587 | ui->hx = tx; | ||
1588 | ui->hy = ty; | ||
1589 | ui->ohx = otx; | ||
1590 | ui->ohy = oty; | ||
1591 | ui->odx = ui->ody = 0; | ||
1592 | ui->odn = 1; | ||
1593 | ui->hshow = !state->common->immutable[ty*w+tx]; | ||
1594 | ui->hpencil = false; | ||
1595 | } | ||
1596 | ui->hcursor = false; | ||
1597 | return MOVE_UI_UPDATE; | ||
1598 | } | ||
1599 | if (button == RIGHT_BUTTON) { | ||
1600 | /* | ||
1601 | * Pencil-mode highlighting for non filled squares. | ||
1602 | */ | ||
1603 | if (state->grid[ty*w+tx] == 0) { | ||
1604 | if (tx == ui->hx && ty == ui->hy && | ||
1605 | ui->hshow && ui->hpencil) { | ||
1606 | ui->hshow = false; | ||
1607 | } else { | ||
1608 | ui->hpencil = true; | ||
1609 | ui->hx = tx; | ||
1610 | ui->hy = ty; | ||
1611 | ui->ohx = otx; | ||
1612 | ui->ohy = oty; | ||
1613 | ui->odx = ui->ody = 0; | ||
1614 | ui->odn = 1; | ||
1615 | ui->hshow = true; | ||
1616 | } | ||
1617 | } else { | ||
1618 | ui->hshow = false; | ||
1619 | } | ||
1620 | ui->hcursor = false; | ||
1621 | return MOVE_UI_UPDATE; | ||
1622 | } | ||
1623 | } else if (tx >= 0 && tx < w && ty == -1) { | ||
1624 | ui->drag = 2; | ||
1625 | ui->dragnum = state->sequence[tx]; | ||
1626 | ui->dragpos = tx; | ||
1627 | ui->edgepos = FROMCOORD(x + TILESIZE/2); | ||
1628 | return MOVE_UI_UPDATE; | ||
1629 | } else if (ty >= 0 && ty < w && tx == -1) { | ||
1630 | ui->drag = 1; | ||
1631 | ui->dragnum = state->sequence[ty]; | ||
1632 | ui->dragpos = ty; | ||
1633 | ui->edgepos = FROMCOORD(y + TILESIZE/2); | ||
1634 | return MOVE_UI_UPDATE; | ||
1635 | } | ||
1636 | } else if (IS_MOUSE_DRAG(button)) { | ||
1637 | if (!ui->hpencil && | ||
1638 | tx >= 0 && tx < w && ty >= 0 && ty < w && | ||
1639 | abs(tx - ui->ohx) == abs(ty - ui->ohy)) { | ||
1640 | ui->odn = abs(tx - ui->ohx) + 1; | ||
1641 | ui->odx = (tx < ui->ohx ? -1 : +1); | ||
1642 | ui->ody = (ty < ui->ohy ? -1 : +1); | ||
1643 | } else { | ||
1644 | ui->odx = ui->ody = 0; | ||
1645 | ui->odn = 1; | ||
1646 | } | ||
1647 | return MOVE_UI_UPDATE; | ||
1648 | } | ||
1649 | |||
1650 | if (IS_CURSOR_MOVE(button)) { | ||
1651 | int cx = find_in_sequence(state->sequence, w, ui->hx); | ||
1652 | int cy = find_in_sequence(state->sequence, w, ui->hy); | ||
1653 | move_cursor(button, &cx, &cy, w, w, false, NULL); | ||
1654 | ui->hx = state->sequence[cx]; | ||
1655 | ui->hy = state->sequence[cy]; | ||
1656 | ui->hshow = true; | ||
1657 | ui->hcursor = true; | ||
1658 | ui->ohx = cx; | ||
1659 | ui->ohy = cy; | ||
1660 | ui->odx = ui->ody = 0; | ||
1661 | ui->odn = 1; | ||
1662 | return MOVE_UI_UPDATE; | ||
1663 | } | ||
1664 | if (ui->hshow && | ||
1665 | (button == CURSOR_SELECT)) { | ||
1666 | ui->hpencil = !ui->hpencil; | ||
1667 | ui->hcursor = true; | ||
1668 | return MOVE_UI_UPDATE; | ||
1669 | } | ||
1670 | |||
1671 | if (ui->hshow && | ||
1672 | ((ISCHAR(button) && FROMCHAR(button, state->par.id) <= w) || | ||
1673 | button == CURSOR_SELECT2 || button == '\b')) { | ||
1674 | int n = FROMCHAR(button, state->par.id); | ||
1675 | int i, buflen; | ||
1676 | char *movebuf; | ||
1677 | |||
1678 | if (button == CURSOR_SELECT2 || button == '\b') | ||
1679 | n = 0; | ||
1680 | |||
1681 | for (i = 0; i < ui->odn; i++) { | ||
1682 | int x = state->sequence[ui->ohx + i*ui->odx]; | ||
1683 | int y = state->sequence[ui->ohy + i*ui->ody]; | ||
1684 | int index = y*w+x; | ||
1685 | |||
1686 | /* | ||
1687 | * Can't make pencil marks in a filled square. This can only | ||
1688 | * become highlighted if we're using cursor keys. | ||
1689 | */ | ||
1690 | if (ui->hpencil && state->grid[index]) | ||
1691 | return NULL; | ||
1692 | |||
1693 | /* | ||
1694 | * Can't do anything to an immutable square. Exception: | ||
1695 | * trying to set it to what it already was is OK (so that | ||
1696 | * multifilling can set a whole diagonal to a without | ||
1697 | * having to detour round the one immutable square in the | ||
1698 | * middle that already said a). | ||
1699 | */ | ||
1700 | if (!ui->hpencil && state->grid[index] == n) | ||
1701 | /* OK even if it is immutable */; | ||
1702 | else if (state->common->immutable[index]) | ||
1703 | return NULL; | ||
1704 | } | ||
1705 | |||
1706 | movebuf = snewn(80 * ui->odn, char); | ||
1707 | buflen = sprintf(movebuf, "%c%d,%d,%d", | ||
1708 | (char)(ui->hpencil && n > 0 ? 'P' : 'R'), | ||
1709 | ui->hx, ui->hy, n); | ||
1710 | for (i = 1; i < ui->odn; i++) { | ||
1711 | assert(buflen < i*80); | ||
1712 | buflen += sprintf(movebuf + buflen, "+%d,%d", | ||
1713 | state->sequence[ui->ohx + i*ui->odx], | ||
1714 | state->sequence[ui->ohy + i*ui->ody]); | ||
1715 | } | ||
1716 | movebuf = sresize(movebuf, buflen+1, char); | ||
1717 | |||
1718 | /* | ||
1719 | * Hide the highlight after a keypress, if it was mouse- | ||
1720 | * generated. Also, don't hide it if this move has changed | ||
1721 | * pencil marks and the user preference says not to hide the | ||
1722 | * highlight in that situation. | ||
1723 | */ | ||
1724 | if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight)) | ||
1725 | ui->hshow = false; | ||
1726 | |||
1727 | return movebuf; | ||
1728 | } | ||
1729 | |||
1730 | if (button == 'M' || button == 'm') | ||
1731 | return dupstr("M"); | ||
1732 | |||
1733 | return NULL; | ||
1734 | } | ||
1735 | |||
1736 | static game_state *execute_move(const game_state *from, const char *move) | ||
1737 | { | ||
1738 | int w = from->par.w, a = w*w; | ||
1739 | game_state *ret; | ||
1740 | int x, y, i, j, n, pos; | ||
1741 | |||
1742 | if (move[0] == 'S') { | ||
1743 | ret = dup_game(from); | ||
1744 | ret->completed = ret->cheated = true; | ||
1745 | |||
1746 | for (i = 0; i < a; i++) { | ||
1747 | if (!ISCHAR(move[i+1]) || FROMCHAR(move[i+1], from->par.id) > w) { | ||
1748 | free_game(ret); | ||
1749 | return NULL; | ||
1750 | } | ||
1751 | ret->grid[i] = FROMCHAR(move[i+1], from->par.id); | ||
1752 | ret->pencil[i] = 0; | ||
1753 | } | ||
1754 | |||
1755 | if (move[a+1] != '\0') { | ||
1756 | free_game(ret); | ||
1757 | return NULL; | ||
1758 | } | ||
1759 | |||
1760 | return ret; | ||
1761 | } else if ((move[0] == 'P' || move[0] == 'R') && | ||
1762 | sscanf(move+1, "%d,%d,%d%n", &x, &y, &n, &pos) == 3 && | ||
1763 | n >= 0 && n <= w) { | ||
1764 | const char *mp = move + 1 + pos; | ||
1765 | bool pencil = (move[0] == 'P'); | ||
1766 | ret = dup_game(from); | ||
1767 | |||
1768 | while (1) { | ||
1769 | if (x < 0 || x >= w || y < 0 || y >= w) { | ||
1770 | free_game(ret); | ||
1771 | return NULL; | ||
1772 | } | ||
1773 | if (from->common->immutable[y*w+x] && | ||
1774 | !(!pencil && from->grid[y*w+x] == n)) | ||
1775 | return NULL; | ||
1776 | |||
1777 | if (move[0] == 'P' && n > 0) { | ||
1778 | ret->pencil[y*w+x] ^= 1 << n; | ||
1779 | } else { | ||
1780 | ret->grid[y*w+x] = n; | ||
1781 | ret->pencil[y*w+x] = 0; | ||
1782 | } | ||
1783 | |||
1784 | if (!*mp) | ||
1785 | break; | ||
1786 | |||
1787 | if (*mp != '+') | ||
1788 | return NULL; | ||
1789 | if (sscanf(mp, "+%d,%d%n", &x, &y, &pos) < 2) | ||
1790 | return NULL; | ||
1791 | mp += pos; | ||
1792 | } | ||
1793 | |||
1794 | if (!ret->completed && !check_errors(ret, NULL)) | ||
1795 | ret->completed = true; | ||
1796 | |||
1797 | return ret; | ||
1798 | } else if (move[0] == 'M') { | ||
1799 | /* | ||
1800 | * Fill in absolutely all pencil marks everywhere. (I | ||
1801 | * wouldn't use this for actual play, but it's a handy | ||
1802 | * starting point when following through a set of | ||
1803 | * diagnostics output by the standalone solver.) | ||
1804 | */ | ||
1805 | ret = dup_game(from); | ||
1806 | for (i = 0; i < a; i++) { | ||
1807 | if (!ret->grid[i]) | ||
1808 | ret->pencil[i] = (1 << (w+1)) - (1 << 1); | ||
1809 | } | ||
1810 | return ret; | ||
1811 | } else if (move[0] == 'D' && | ||
1812 | sscanf(move+1, "%d,%d", &x, &y) == 2) { | ||
1813 | /* | ||
1814 | * Reorder the rows and columns so that digit x is in position | ||
1815 | * y. | ||
1816 | */ | ||
1817 | ret = dup_game(from); | ||
1818 | for (i = j = 0; i < w; i++) { | ||
1819 | if (i == y) { | ||
1820 | ret->sequence[i] = x; | ||
1821 | } else { | ||
1822 | if (from->sequence[j] == x) | ||
1823 | j++; | ||
1824 | ret->sequence[i] = from->sequence[j++]; | ||
1825 | } | ||
1826 | } | ||
1827 | /* | ||
1828 | * Eliminate any obsoleted dividers. | ||
1829 | */ | ||
1830 | for (x = 0; x < w; x++) { | ||
1831 | int i = ret->sequence[x]; | ||
1832 | int j = (x+1 < w ? ret->sequence[x+1] : -1); | ||
1833 | if (ret->dividers[i] != j) | ||
1834 | ret->dividers[i] = -1; | ||
1835 | } | ||
1836 | return ret; | ||
1837 | } else if (move[0] == 'V' && | ||
1838 | sscanf(move+1, "%d,%d", &i, &j) == 2) { | ||
1839 | ret = dup_game(from); | ||
1840 | if (ret->dividers[i] == j) | ||
1841 | ret->dividers[i] = -1; | ||
1842 | else | ||
1843 | ret->dividers[i] = j; | ||
1844 | return ret; | ||
1845 | } else | ||
1846 | return NULL; /* couldn't parse move string */ | ||
1847 | } | ||
1848 | |||
1849 | /* ---------------------------------------------------------------------- | ||
1850 | * Drawing routines. | ||
1851 | */ | ||
1852 | |||
1853 | #define SIZE(w) ((w) * TILESIZE + 2*BORDER + LEGEND) | ||
1854 | |||
1855 | static void game_compute_size(const game_params *params, int tilesize, | ||
1856 | const game_ui *ui, int *x, int *y) | ||
1857 | { | ||
1858 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ | ||
1859 | struct { int tilesize; } ads, *ds = &ads; | ||
1860 | ads.tilesize = tilesize; | ||
1861 | |||
1862 | *x = *y = SIZE(params->w); | ||
1863 | } | ||
1864 | |||
1865 | static void game_set_size(drawing *dr, game_drawstate *ds, | ||
1866 | const game_params *params, int tilesize) | ||
1867 | { | ||
1868 | ds->tilesize = tilesize; | ||
1869 | } | ||
1870 | |||
1871 | static float *game_colours(frontend *fe, int *ncolours) | ||
1872 | { | ||
1873 | float *ret = snewn(3 * NCOLOURS, float); | ||
1874 | |||
1875 | frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); | ||
1876 | |||
1877 | ret[COL_GRID * 3 + 0] = 0.0F; | ||
1878 | ret[COL_GRID * 3 + 1] = 0.0F; | ||
1879 | ret[COL_GRID * 3 + 2] = 0.0F; | ||
1880 | |||
1881 | ret[COL_USER * 3 + 0] = 0.0F; | ||
1882 | ret[COL_USER * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1]; | ||
1883 | ret[COL_USER * 3 + 2] = 0.0F; | ||
1884 | |||
1885 | ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0]; | ||
1886 | ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1]; | ||
1887 | ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2]; | ||
1888 | |||
1889 | ret[COL_ERROR * 3 + 0] = 1.0F; | ||
1890 | ret[COL_ERROR * 3 + 1] = 0.0F; | ||
1891 | ret[COL_ERROR * 3 + 2] = 0.0F; | ||
1892 | |||
1893 | ret[COL_PENCIL * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0]; | ||
1894 | ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1]; | ||
1895 | ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2]; | ||
1896 | |||
1897 | ret[COL_DIAGONAL * 3 + 0] = 0.95F * ret[COL_BACKGROUND * 3 + 0]; | ||
1898 | ret[COL_DIAGONAL * 3 + 1] = 0.95F * ret[COL_BACKGROUND * 3 + 1]; | ||
1899 | ret[COL_DIAGONAL * 3 + 2] = 0.95F * ret[COL_BACKGROUND * 3 + 2]; | ||
1900 | |||
1901 | *ncolours = NCOLOURS; | ||
1902 | return ret; | ||
1903 | } | ||
1904 | |||
1905 | static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) | ||
1906 | { | ||
1907 | int w = state->par.w, a = w*w; | ||
1908 | struct game_drawstate *ds = snew(struct game_drawstate); | ||
1909 | int i; | ||
1910 | |||
1911 | ds->w = w; | ||
1912 | ds->par = state->par; /* structure copy */ | ||
1913 | ds->tilesize = 0; | ||
1914 | ds->started = false; | ||
1915 | ds->tiles = snewn(a, long); | ||
1916 | ds->legend = snewn(w, long); | ||
1917 | ds->pencil = snewn(a, long); | ||
1918 | ds->errors = snewn(a, long); | ||
1919 | ds->sequence = snewn(a, digit); | ||
1920 | for (i = 0; i < a; i++) | ||
1921 | ds->tiles[i] = ds->pencil[i] = -1; | ||
1922 | for (i = 0; i < w; i++) | ||
1923 | ds->legend[i] = -1; | ||
1924 | ds->errtmp = snewn(a, long); | ||
1925 | |||
1926 | return ds; | ||
1927 | } | ||
1928 | |||
1929 | static void game_free_drawstate(drawing *dr, game_drawstate *ds) | ||
1930 | { | ||
1931 | sfree(ds->tiles); | ||
1932 | sfree(ds->pencil); | ||
1933 | sfree(ds->errors); | ||
1934 | sfree(ds->errtmp); | ||
1935 | sfree(ds->sequence); | ||
1936 | sfree(ds); | ||
1937 | } | ||
1938 | |||
1939 | static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, long tile, | ||
1940 | long pencil, long error) | ||
1941 | { | ||
1942 | int w = ds->w /* , a = w*w */; | ||
1943 | int tx, ty, tw, th; | ||
1944 | int cx, cy, cw, ch; | ||
1945 | char str[64]; | ||
1946 | |||
1947 | tx = BORDER + LEGEND + x * TILESIZE + 1; | ||
1948 | ty = BORDER + LEGEND + y * TILESIZE + 1; | ||
1949 | |||
1950 | cx = tx; | ||
1951 | cy = ty; | ||
1952 | cw = tw = TILESIZE-1; | ||
1953 | ch = th = TILESIZE-1; | ||
1954 | |||
1955 | if (tile & DF_LEGEND) { | ||
1956 | cx += TILESIZE/10; | ||
1957 | cy += TILESIZE/10; | ||
1958 | cw -= TILESIZE/5; | ||
1959 | ch -= TILESIZE/5; | ||
1960 | tile |= DF_IMMUTABLE; | ||
1961 | } | ||
1962 | |||
1963 | clip(dr, cx, cy, cw, ch); | ||
1964 | |||
1965 | /* background needs erasing */ | ||
1966 | draw_rect(dr, cx, cy, cw, ch, | ||
1967 | (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : | ||
1968 | (x == y) ? COL_DIAGONAL : COL_BACKGROUND); | ||
1969 | |||
1970 | /* dividers */ | ||
1971 | if (tile & DF_DIVIDER_TOP) | ||
1972 | draw_rect(dr, cx, cy, cw, 1, COL_GRID); | ||
1973 | if (tile & DF_DIVIDER_BOT) | ||
1974 | draw_rect(dr, cx, cy+ch-1, cw, 1, COL_GRID); | ||
1975 | if (tile & DF_DIVIDER_LEFT) | ||
1976 | draw_rect(dr, cx, cy, 1, ch, COL_GRID); | ||
1977 | if (tile & DF_DIVIDER_RIGHT) | ||
1978 | draw_rect(dr, cx+cw-1, cy, 1, ch, COL_GRID); | ||
1979 | |||
1980 | /* pencil-mode highlight */ | ||
1981 | if (tile & DF_HIGHLIGHT_PENCIL) { | ||
1982 | int coords[6]; | ||
1983 | coords[0] = cx; | ||
1984 | coords[1] = cy; | ||
1985 | coords[2] = cx+cw/2; | ||
1986 | coords[3] = cy; | ||
1987 | coords[4] = cx; | ||
1988 | coords[5] = cy+ch/2; | ||
1989 | draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); | ||
1990 | } | ||
1991 | |||
1992 | /* new number needs drawing? */ | ||
1993 | if (tile & DF_DIGIT_MASK) { | ||
1994 | str[1] = '\0'; | ||
1995 | str[0] = TOCHAR(tile & DF_DIGIT_MASK, ds->par.id); | ||
1996 | draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2, | ||
1997 | FONT_VARIABLE, TILESIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE, | ||
1998 | (error & EF_LATIN) ? COL_ERROR : | ||
1999 | (tile & DF_IMMUTABLE) ? COL_GRID : COL_USER, str); | ||
2000 | |||
2001 | if (error & EF_LEFT_MASK) { | ||
2002 | int a = (error >> (EF_LEFT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK; | ||
2003 | int b = (error >> (EF_LEFT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK; | ||
2004 | int c = (error >> (EF_LEFT_SHIFT ))&EF_DIGIT_MASK; | ||
2005 | char buf[10]; | ||
2006 | sprintf(buf, "(%c%c)%c", TOCHAR(a, ds->par.id), | ||
2007 | TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id)); | ||
2008 | draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/6, | ||
2009 | FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE, | ||
2010 | COL_ERROR, buf); | ||
2011 | } | ||
2012 | if (error & EF_RIGHT_MASK) { | ||
2013 | int a = (error >> (EF_RIGHT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK; | ||
2014 | int b = (error >> (EF_RIGHT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK; | ||
2015 | int c = (error >> (EF_RIGHT_SHIFT ))&EF_DIGIT_MASK; | ||
2016 | char buf[10]; | ||
2017 | sprintf(buf, "%c(%c%c)", TOCHAR(a, ds->par.id), | ||
2018 | TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id)); | ||
2019 | draw_text(dr, tx + TILESIZE/2, ty + TILESIZE - TILESIZE/6, | ||
2020 | FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE, | ||
2021 | COL_ERROR, buf); | ||
2022 | } | ||
2023 | } else { | ||
2024 | int i, j, npencil; | ||
2025 | int pl, pr, pt, pb; | ||
2026 | float bestsize; | ||
2027 | int pw, ph, minph, pbest, fontsize; | ||
2028 | |||
2029 | /* Count the pencil marks required. */ | ||
2030 | for (i = 1, npencil = 0; i <= w; i++) | ||
2031 | if (pencil & (1 << i)) | ||
2032 | npencil++; | ||
2033 | if (npencil) { | ||
2034 | |||
2035 | minph = 2; | ||
2036 | |||
2037 | /* | ||
2038 | * Determine the bounding rectangle within which we're going | ||
2039 | * to put the pencil marks. | ||
2040 | */ | ||
2041 | /* Start with the whole square */ | ||
2042 | pl = tx + GRIDEXTRA; | ||
2043 | pr = pl + TILESIZE - GRIDEXTRA; | ||
2044 | pt = ty + GRIDEXTRA; | ||
2045 | pb = pt + TILESIZE - GRIDEXTRA; | ||
2046 | |||
2047 | /* | ||
2048 | * We arrange our pencil marks in a grid layout, with | ||
2049 | * the number of rows and columns adjusted to allow the | ||
2050 | * maximum font size. | ||
2051 | * | ||
2052 | * So now we work out what the grid size ought to be. | ||
2053 | */ | ||
2054 | bestsize = 0.0; | ||
2055 | pbest = 0; | ||
2056 | /* Minimum */ | ||
2057 | for (pw = 3; pw < max(npencil,4); pw++) { | ||
2058 | float fw, fh, fs; | ||
2059 | |||
2060 | ph = (npencil + pw - 1) / pw; | ||
2061 | ph = max(ph, minph); | ||
2062 | fw = (pr - pl) / (float)pw; | ||
2063 | fh = (pb - pt) / (float)ph; | ||
2064 | fs = min(fw, fh); | ||
2065 | if (fs > bestsize) { | ||
2066 | bestsize = fs; | ||
2067 | pbest = pw; | ||
2068 | } | ||
2069 | } | ||
2070 | assert(pbest > 0); | ||
2071 | pw = pbest; | ||
2072 | ph = (npencil + pw - 1) / pw; | ||
2073 | ph = max(ph, minph); | ||
2074 | |||
2075 | /* | ||
2076 | * Now we've got our grid dimensions, work out the pixel | ||
2077 | * size of a grid element, and round it to the nearest | ||
2078 | * pixel. (We don't want rounding errors to make the | ||
2079 | * grid look uneven at low pixel sizes.) | ||
2080 | */ | ||
2081 | fontsize = min((pr - pl) / pw, (pb - pt) / ph); | ||
2082 | |||
2083 | /* | ||
2084 | * Centre the resulting figure in the square. | ||
2085 | */ | ||
2086 | pl = tx + (TILESIZE - fontsize * pw) / 2; | ||
2087 | pt = ty + (TILESIZE - fontsize * ph) / 2; | ||
2088 | |||
2089 | /* | ||
2090 | * Now actually draw the pencil marks. | ||
2091 | */ | ||
2092 | for (i = 1, j = 0; i <= w; i++) | ||
2093 | if (pencil & (1 << i)) { | ||
2094 | int dx = j % pw, dy = j / pw; | ||
2095 | |||
2096 | str[1] = '\0'; | ||
2097 | str[0] = TOCHAR(i, ds->par.id); | ||
2098 | draw_text(dr, pl + fontsize * (2*dx+1) / 2, | ||
2099 | pt + fontsize * (2*dy+1) / 2, | ||
2100 | FONT_VARIABLE, fontsize, | ||
2101 | ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str); | ||
2102 | j++; | ||
2103 | } | ||
2104 | } | ||
2105 | } | ||
2106 | |||
2107 | unclip(dr); | ||
2108 | |||
2109 | draw_update(dr, cx, cy, cw, ch); | ||
2110 | } | ||
2111 | |||
2112 | static void game_redraw(drawing *dr, game_drawstate *ds, | ||
2113 | const game_state *oldstate, const game_state *state, | ||
2114 | int dir, const game_ui *ui, | ||
2115 | float animtime, float flashtime) | ||
2116 | { | ||
2117 | int w = state->par.w /*, a = w*w */; | ||
2118 | int x, y, i, j; | ||
2119 | |||
2120 | if (!ds->started) { | ||
2121 | /* | ||
2122 | * Big containing rectangle. | ||
2123 | */ | ||
2124 | draw_rect(dr, COORD(0) - GRIDEXTRA, COORD(0) - GRIDEXTRA, | ||
2125 | w*TILESIZE+1+GRIDEXTRA*2, w*TILESIZE+1+GRIDEXTRA*2, | ||
2126 | COL_GRID); | ||
2127 | |||
2128 | draw_update(dr, 0, 0, SIZE(w), SIZE(w)); | ||
2129 | |||
2130 | ds->started = true; | ||
2131 | } | ||
2132 | |||
2133 | check_errors(state, ds->errtmp); | ||
2134 | |||
2135 | /* | ||
2136 | * Construct a modified version of state->sequence which takes | ||
2137 | * into account an unfinished drag operation. | ||
2138 | */ | ||
2139 | if (ui->drag) { | ||
2140 | x = ui->dragnum; | ||
2141 | y = ui->dragpos; | ||
2142 | } else { | ||
2143 | x = y = -1; | ||
2144 | } | ||
2145 | for (i = j = 0; i < w; i++) { | ||
2146 | if (i == y) { | ||
2147 | ds->sequence[i] = x; | ||
2148 | } else { | ||
2149 | if (state->sequence[j] == x) | ||
2150 | j++; | ||
2151 | ds->sequence[i] = state->sequence[j++]; | ||
2152 | } | ||
2153 | } | ||
2154 | |||
2155 | /* | ||
2156 | * Draw the table legend. | ||
2157 | */ | ||
2158 | for (x = 0; x < w; x++) { | ||
2159 | int sx = ds->sequence[x]; | ||
2160 | long tile = (sx+1) | DF_LEGEND; | ||
2161 | if (ds->legend[x] != tile) { | ||
2162 | ds->legend[x] = tile; | ||
2163 | draw_tile(dr, ds, -1, x, tile, 0, 0); | ||
2164 | draw_tile(dr, ds, x, -1, tile, 0, 0); | ||
2165 | } | ||
2166 | } | ||
2167 | |||
2168 | for (y = 0; y < w; y++) { | ||
2169 | int sy = ds->sequence[y]; | ||
2170 | for (x = 0; x < w; x++) { | ||
2171 | long tile = 0L, pencil = 0L, error; | ||
2172 | int sx = ds->sequence[x]; | ||
2173 | |||
2174 | if (state->grid[sy*w+sx]) | ||
2175 | tile = state->grid[sy*w+sx]; | ||
2176 | else | ||
2177 | pencil = (long)state->pencil[sy*w+sx]; | ||
2178 | |||
2179 | if (state->common->immutable[sy*w+sx]) | ||
2180 | tile |= DF_IMMUTABLE; | ||
2181 | |||
2182 | if ((ui->drag == 5 && ui->dragnum == sy) || | ||
2183 | (ui->drag == 6 && ui->dragnum == sx)) { | ||
2184 | tile |= DF_HIGHLIGHT; | ||
2185 | } else if (ui->hshow) { | ||
2186 | int i = abs(x - ui->ohx); | ||
2187 | bool highlight = false; | ||
2188 | if (ui->odn > 1) { | ||
2189 | /* | ||
2190 | * When a diagonal multifill selection is shown, | ||
2191 | * we show it in its original grid position | ||
2192 | * regardless of in-progress row/col drags. Moving | ||
2193 | * every square about would be horrible. | ||
2194 | */ | ||
2195 | if (i >= 0 && i < ui->odn && | ||
2196 | x == ui->ohx + i*ui->odx && | ||
2197 | y == ui->ohy + i*ui->ody) | ||
2198 | highlight = true; | ||
2199 | } else { | ||
2200 | /* | ||
2201 | * For a single square, we move its highlight | ||
2202 | * around with the drag. | ||
2203 | */ | ||
2204 | highlight = (ui->hx == sx && ui->hy == sy); | ||
2205 | } | ||
2206 | if (highlight) | ||
2207 | tile |= (ui->hpencil ? DF_HIGHLIGHT_PENCIL : DF_HIGHLIGHT); | ||
2208 | } | ||
2209 | |||
2210 | if (flashtime > 0 && | ||
2211 | (flashtime <= FLASH_TIME/3 || | ||
2212 | flashtime >= FLASH_TIME*2/3)) | ||
2213 | tile |= DF_HIGHLIGHT; /* completion flash */ | ||
2214 | |||
2215 | if (y <= 0 || state->dividers[ds->sequence[y-1]] == sy) | ||
2216 | tile |= DF_DIVIDER_TOP; | ||
2217 | if (y+1 >= w || state->dividers[sy] == ds->sequence[y+1]) | ||
2218 | tile |= DF_DIVIDER_BOT; | ||
2219 | if (x <= 0 || state->dividers[ds->sequence[x-1]] == sx) | ||
2220 | tile |= DF_DIVIDER_LEFT; | ||
2221 | if (x+1 >= w || state->dividers[sx] == ds->sequence[x+1]) | ||
2222 | tile |= DF_DIVIDER_RIGHT; | ||
2223 | |||
2224 | error = ds->errtmp[sy*w+sx]; | ||
2225 | |||
2226 | if (ds->tiles[y*w+x] != tile || | ||
2227 | ds->pencil[y*w+x] != pencil || | ||
2228 | ds->errors[y*w+x] != error) { | ||
2229 | ds->tiles[y*w+x] = tile; | ||
2230 | ds->pencil[y*w+x] = pencil; | ||
2231 | ds->errors[y*w+x] = error; | ||
2232 | draw_tile(dr, ds, x, y, tile, pencil, error); | ||
2233 | } | ||
2234 | } | ||
2235 | } | ||
2236 | } | ||
2237 | |||
2238 | static float game_anim_length(const game_state *oldstate, | ||
2239 | const game_state *newstate, int dir, game_ui *ui) | ||
2240 | { | ||
2241 | return 0.0F; | ||
2242 | } | ||
2243 | |||
2244 | static float game_flash_length(const game_state *oldstate, | ||
2245 | const game_state *newstate, int dir, game_ui *ui) | ||
2246 | { | ||
2247 | if (!oldstate->completed && newstate->completed && | ||
2248 | !oldstate->cheated && !newstate->cheated) | ||
2249 | return FLASH_TIME; | ||
2250 | return 0.0F; | ||
2251 | } | ||
2252 | |||
2253 | static void game_get_cursor_location(const game_ui *ui, | ||
2254 | const game_drawstate *ds, | ||
2255 | const game_state *state, | ||
2256 | const game_params *params, | ||
2257 | int *x, int *y, int *w, int *h) | ||
2258 | { | ||
2259 | } | ||
2260 | |||
2261 | static int game_status(const game_state *state) | ||
2262 | { | ||
2263 | return state->completed ? +1 : 0; | ||
2264 | } | ||
2265 | |||
2266 | static bool game_timing_state(const game_state *state, game_ui *ui) | ||
2267 | { | ||
2268 | if (state->completed) | ||
2269 | return false; | ||
2270 | return true; | ||
2271 | } | ||
2272 | |||
2273 | static void game_print_size(const game_params *params, const game_ui *ui, | ||
2274 | float *x, float *y) | ||
2275 | { | ||
2276 | int pw, ph; | ||
2277 | |||
2278 | /* | ||
2279 | * We use 9mm squares by default, like Solo. | ||
2280 | */ | ||
2281 | game_compute_size(params, 900, ui, &pw, &ph); | ||
2282 | *x = pw / 100.0F; | ||
2283 | *y = ph / 100.0F; | ||
2284 | } | ||
2285 | |||
2286 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, | ||
2287 | int tilesize) | ||
2288 | { | ||
2289 | int w = state->par.w; | ||
2290 | int ink = print_mono_colour(dr, 0); | ||
2291 | int x, y; | ||
2292 | |||
2293 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ | ||
2294 | game_drawstate ads, *ds = &ads; | ||
2295 | game_set_size(dr, ds, NULL, tilesize); | ||
2296 | |||
2297 | /* | ||
2298 | * Border. | ||
2299 | */ | ||
2300 | print_line_width(dr, 3 * TILESIZE / 40); | ||
2301 | draw_rect_outline(dr, BORDER + LEGEND, BORDER + LEGEND, | ||
2302 | w*TILESIZE, w*TILESIZE, ink); | ||
2303 | |||
2304 | /* | ||
2305 | * Legend on table. | ||
2306 | */ | ||
2307 | for (x = 0; x < w; x++) { | ||
2308 | char str[2]; | ||
2309 | str[1] = '\0'; | ||
2310 | str[0] = TOCHAR(x+1, state->par.id); | ||
2311 | draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2, | ||
2312 | BORDER + TILESIZE/2, | ||
2313 | FONT_VARIABLE, TILESIZE/2, | ||
2314 | ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); | ||
2315 | draw_text(dr, BORDER + TILESIZE/2, | ||
2316 | BORDER+LEGEND + x*TILESIZE + TILESIZE/2, | ||
2317 | FONT_VARIABLE, TILESIZE/2, | ||
2318 | ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); | ||
2319 | } | ||
2320 | |||
2321 | /* | ||
2322 | * Main grid. | ||
2323 | */ | ||
2324 | for (x = 1; x < w; x++) { | ||
2325 | print_line_width(dr, TILESIZE / 40); | ||
2326 | draw_line(dr, BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND, | ||
2327 | BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND+w*TILESIZE, ink); | ||
2328 | } | ||
2329 | for (y = 1; y < w; y++) { | ||
2330 | print_line_width(dr, TILESIZE / 40); | ||
2331 | draw_line(dr, BORDER+LEGEND, BORDER+LEGEND+y*TILESIZE, | ||
2332 | BORDER+LEGEND+w*TILESIZE, BORDER+LEGEND+y*TILESIZE, ink); | ||
2333 | } | ||
2334 | |||
2335 | /* | ||
2336 | * Numbers. | ||
2337 | */ | ||
2338 | for (y = 0; y < w; y++) | ||
2339 | for (x = 0; x < w; x++) | ||
2340 | if (state->grid[y*w+x]) { | ||
2341 | char str[2]; | ||
2342 | str[1] = '\0'; | ||
2343 | str[0] = TOCHAR(state->grid[y*w+x], state->par.id); | ||
2344 | draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2, | ||
2345 | BORDER+LEGEND + y*TILESIZE + TILESIZE/2, | ||
2346 | FONT_VARIABLE, TILESIZE/2, | ||
2347 | ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); | ||
2348 | } | ||
2349 | } | ||
2350 | |||
2351 | #ifdef COMBINED | ||
2352 | #define thegame group | ||
2353 | #endif | ||
2354 | |||
2355 | const struct game thegame = { | ||
2356 | "Group", NULL, NULL, | ||
2357 | default_params, | ||
2358 | game_fetch_preset, NULL, | ||
2359 | decode_params, | ||
2360 | encode_params, | ||
2361 | free_params, | ||
2362 | dup_params, | ||
2363 | true, game_configure, custom_params, | ||
2364 | validate_params, | ||
2365 | new_game_desc, | ||
2366 | validate_desc, | ||
2367 | new_game, | ||
2368 | dup_game, | ||
2369 | free_game, | ||
2370 | true, solve_game, | ||
2371 | true, game_can_format_as_text_now, game_text_format, | ||
2372 | get_prefs, set_prefs, | ||
2373 | new_ui, | ||
2374 | free_ui, | ||
2375 | NULL, /* encode_ui */ | ||
2376 | NULL, /* decode_ui */ | ||
2377 | NULL, /* game_request_keys */ | ||
2378 | game_changed_state, | ||
2379 | current_key_label, | ||
2380 | interpret_move, | ||
2381 | execute_move, | ||
2382 | PREFERRED_TILESIZE, game_compute_size, game_set_size, | ||
2383 | game_colours, | ||
2384 | game_new_drawstate, | ||
2385 | game_free_drawstate, | ||
2386 | game_redraw, | ||
2387 | game_anim_length, | ||
2388 | game_flash_length, | ||
2389 | game_get_cursor_location, | ||
2390 | game_status, | ||
2391 | true, false, game_print_size, game_print, | ||
2392 | false, /* wants_statusbar */ | ||
2393 | false, game_timing_state, | ||
2394 | REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ | ||
2395 | }; | ||
2396 | |||
2397 | #ifdef STANDALONE_SOLVER | ||
2398 | |||
2399 | #include <stdarg.h> | ||
2400 | |||
2401 | int main(int argc, char **argv) | ||
2402 | { | ||
2403 | game_params *p; | ||
2404 | game_state *s; | ||
2405 | char *id = NULL, *desc; | ||
2406 | const char *err; | ||
2407 | digit *grid; | ||
2408 | bool grade = false; | ||
2409 | int ret, diff; | ||
2410 | bool really_show_working = false; | ||
2411 | |||
2412 | while (--argc > 0) { | ||
2413 | char *p = *++argv; | ||
2414 | if (!strcmp(p, "-v")) { | ||
2415 | really_show_working = true; | ||
2416 | } else if (!strcmp(p, "-g")) { | ||
2417 | grade = true; | ||
2418 | } else if (*p == '-') { | ||
2419 | fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); | ||
2420 | return 1; | ||
2421 | } else { | ||
2422 | id = p; | ||
2423 | } | ||
2424 | } | ||
2425 | |||
2426 | if (!id) { | ||
2427 | fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]); | ||
2428 | return 1; | ||
2429 | } | ||
2430 | |||
2431 | desc = strchr(id, ':'); | ||
2432 | if (!desc) { | ||
2433 | fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]); | ||
2434 | return 1; | ||
2435 | } | ||
2436 | *desc++ = '\0'; | ||
2437 | |||
2438 | p = default_params(); | ||
2439 | decode_params(p, id); | ||
2440 | err = validate_desc(p, desc); | ||
2441 | if (err) { | ||
2442 | fprintf(stderr, "%s: %s\n", argv[0], err); | ||
2443 | return 1; | ||
2444 | } | ||
2445 | s = new_game(NULL, p, desc); | ||
2446 | |||
2447 | grid = snewn(p->w * p->w, digit); | ||
2448 | |||
2449 | /* | ||
2450 | * When solving a Normal puzzle, we don't want to bother the | ||
2451 | * user with Hard-level deductions. For this reason, we grade | ||
2452 | * the puzzle internally before doing anything else. | ||
2453 | */ | ||
2454 | ret = -1; /* placate optimiser */ | ||
2455 | solver_show_working = 0; | ||
2456 | for (diff = 0; diff < DIFFCOUNT; diff++) { | ||
2457 | memcpy(grid, s->grid, p->w * p->w); | ||
2458 | ret = solver(&s->par, grid, diff); | ||
2459 | if (ret <= diff) | ||
2460 | break; | ||
2461 | } | ||
2462 | |||
2463 | if (diff == DIFFCOUNT) { | ||
2464 | if (really_show_working) { | ||
2465 | solver_show_working = true; | ||
2466 | memcpy(grid, s->grid, p->w * p->w); | ||
2467 | ret = solver(&s->par, grid, DIFFCOUNT - 1); | ||
2468 | } | ||
2469 | if (grade) | ||
2470 | printf("Difficulty rating: ambiguous\n"); | ||
2471 | else | ||
2472 | printf("Unable to find a unique solution\n"); | ||
2473 | } else { | ||
2474 | if (grade) { | ||
2475 | if (ret == diff_impossible) | ||
2476 | printf("Difficulty rating: impossible (no solution exists)\n"); | ||
2477 | else | ||
2478 | printf("Difficulty rating: %s\n", group_diffnames[ret]); | ||
2479 | } else { | ||
2480 | solver_show_working = really_show_working; | ||
2481 | memcpy(grid, s->grid, p->w * p->w); | ||
2482 | ret = solver(&s->par, grid, diff); | ||
2483 | if (ret != diff) | ||
2484 | printf("Puzzle is inconsistent\n"); | ||
2485 | else { | ||
2486 | memcpy(s->grid, grid, p->w * p->w); | ||
2487 | fputs(game_text_format(s), stdout); | ||
2488 | } | ||
2489 | } | ||
2490 | } | ||
2491 | |||
2492 | return 0; | ||
2493 | } | ||
2494 | |||
2495 | #endif | ||
2496 | |||
2497 | /* vim: set shiftwidth=4 tabstop=8: */ | ||
diff --git a/apps/plugins/puzzles/src/unfinished/group.gap b/apps/plugins/puzzles/src/unfinished/group.gap new file mode 100644 index 0000000000..280adf4664 --- /dev/null +++ b/apps/plugins/puzzles/src/unfinished/group.gap | |||
@@ -0,0 +1,97 @@ | |||
1 | # run this file with | ||
2 | # gap -b -q < /dev/null group.gap | perl -pe 's/\\\n//s' | indent -kr | ||
3 | |||
4 | Print("/* ----- data generated by group.gap begins ----- */\n\n"); | ||
5 | Print("struct group {\n unsigned long autosize;\n"); | ||
6 | Print(" int order, ngens;\n const char *gens;\n};\n"); | ||
7 | Print("struct groups {\n int ngroups;\n"); | ||
8 | Print(" const struct group *groups;\n};\n\n"); | ||
9 | Print("static const struct group groupdata[] = {\n"); | ||
10 | offsets := [0]; | ||
11 | offset := 0; | ||
12 | for n in [2..26] do | ||
13 | Print(" /* order ", n, " */\n"); | ||
14 | for G in AllSmallGroups(n) do | ||
15 | |||
16 | # Construct a representation of the group G as a subgroup | ||
17 | # of a permutation group, and find its generators in that | ||
18 | # group. | ||
19 | |||
20 | # GAP has the 'IsomorphismPermGroup' function, but I don't want | ||
21 | # to use it because it doesn't guarantee that the permutation | ||
22 | # representation of the group forms a Cayley table. For example, | ||
23 | # C_4 could be represented as a subgroup of S_4 in many ways, | ||
24 | # and not all of them work: the group generated by (12) and (34) | ||
25 | # is clearly isomorphic to C_4 but its four elements do not form | ||
26 | # a Cayley table. The group generated by (12)(34) and (13)(24) | ||
27 | # is OK, though. | ||
28 | # | ||
29 | # Hence I construct the permutation representation _as_ the | ||
30 | # Cayley table, and then pick generators of that. This | ||
31 | # guarantees that when we rebuild the full group by BFS in | ||
32 | # group.c, we will end up with the right thing. | ||
33 | |||
34 | ge := Elements(G); | ||
35 | gi := []; | ||
36 | for g in ge do | ||
37 | gr := []; | ||
38 | for h in ge do | ||
39 | k := g*h; | ||
40 | for i in [1..n] do | ||
41 | if k = ge[i] then | ||
42 | Add(gr, i); | ||
43 | fi; | ||
44 | od; | ||
45 | od; | ||
46 | Add(gi, PermList(gr)); | ||
47 | od; | ||
48 | |||
49 | # GAP has the 'GeneratorsOfGroup' function, but we don't want to | ||
50 | # use it because it's bad at picking generators - it thinks the | ||
51 | # generators of C_4 are [ (1,2)(3,4), (1,3,2,4) ] and that those | ||
52 | # of C_6 are [ (1,2,3)(4,5,6), (1,4)(2,5)(3,6) ] ! | ||
53 | |||
54 | gl := ShallowCopy(Elements(gi)); | ||
55 | Sort(gl, function(v,w) return Order(v) > Order(w); end); | ||
56 | |||
57 | gens := []; | ||
58 | for x in gl do | ||
59 | if gens = [] or not (x in gp) then | ||
60 | Add(gens, x); | ||
61 | gp := GroupWithGenerators(gens); | ||
62 | fi; | ||
63 | od; | ||
64 | |||
65 | # Construct the C representation of the group generators. | ||
66 | s := []; | ||
67 | for x in gens do | ||
68 | if Size(s) > 0 then | ||
69 | Add(s, '"'); | ||
70 | Add(s, ' '); | ||
71 | Add(s, '"'); | ||
72 | fi; | ||
73 | sep := "\\0"; | ||
74 | for i in ListPerm(x) do | ||
75 | chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
76 | Add(s, chars[i]); | ||
77 | od; | ||
78 | od; | ||
79 | s := JoinStringsWithSeparator([" {", String(Size(AutomorphismGroup(G))), | ||
80 | "L, ", String(Size(G)), | ||
81 | ", ", String(Size(gens)), | ||
82 | ", \"", s, "\"},\n"],""); | ||
83 | Print(s); | ||
84 | offset := offset + 1; | ||
85 | od; | ||
86 | Add(offsets, offset); | ||
87 | od; | ||
88 | Print("};\n\nstatic const struct groups groups[] = {\n"); | ||
89 | Print(" {0, NULL}, /* trivial case: 0 */\n"); | ||
90 | Print(" {0, NULL}, /* trivial case: 1 */\n"); | ||
91 | n := 2; | ||
92 | for i in [1..Size(offsets)-1] do | ||
93 | Print(" {", offsets[i+1] - offsets[i], ", groupdata+", | ||
94 | offsets[i], "}, /* ", i+1, " */\n"); | ||
95 | od; | ||
96 | Print("};\n\n/* ----- data generated by group.gap ends ----- */\n"); | ||
97 | quit; | ||
diff --git a/apps/plugins/puzzles/src/unfinished/numgame.c b/apps/plugins/puzzles/src/unfinished/numgame.c new file mode 100644 index 0000000000..cf919922e7 --- /dev/null +++ b/apps/plugins/puzzles/src/unfinished/numgame.c | |||
@@ -0,0 +1,1294 @@ | |||
1 | /* | ||
2 | * This program implements a breadth-first search which | ||
3 | * exhaustively solves the Countdown numbers game, and related | ||
4 | * games with slightly different rule sets such as `Flippo'. | ||
5 | * | ||
6 | * Currently it is simply a standalone command-line utility to | ||
7 | * which you provide a set of numbers and it tells you everything | ||
8 | * it can make together with how many different ways it can be | ||
9 | * made. I would like ultimately to turn it into the generator for | ||
10 | * a Puzzles puzzle, but I haven't even started on writing a | ||
11 | * Puzzles user interface yet. | ||
12 | */ | ||
13 | |||
14 | /* | ||
15 | * TODO: | ||
16 | * | ||
17 | * - start thinking about difficulty ratings | ||
18 | * + anything involving associative operations will be flagged | ||
19 | * as many-paths because of the associative options (e.g. | ||
20 | * 2*3*4 can be (2*3)*4 or 2*(3*4), or indeed (2*4)*3). This | ||
21 | * is probably a _good_ thing, since those are unusually | ||
22 | * easy. | ||
23 | * + tree-structured calculations ((a*b)/(c+d)) have multiple | ||
24 | * paths because the independent branches of the tree can be | ||
25 | * evaluated in either order, whereas straight-line | ||
26 | * calculations with no branches will be considered easier. | ||
27 | * Can we do anything about this? It's certainly not clear to | ||
28 | * me that tree-structure calculations are _easier_, although | ||
29 | * I'm also not convinced they're harder. | ||
30 | * + I think for a realistic difficulty assessment we must also | ||
31 | * consider the `obviousness' of the arithmetic operations in | ||
32 | * some heuristic sense, and also (in Countdown) how many | ||
33 | * numbers ended up being used. | ||
34 | * - actually try some generations | ||
35 | * - at this point we're probably ready to start on the Puzzles | ||
36 | * integration. | ||
37 | */ | ||
38 | |||
39 | #include <stdio.h> | ||
40 | #include <string.h> | ||
41 | #include <limits.h> | ||
42 | #include <assert.h> | ||
43 | #ifdef NO_TGMATH_H | ||
44 | # include <math.h> | ||
45 | #else | ||
46 | # include <tgmath.h> | ||
47 | #endif | ||
48 | |||
49 | #include "puzzles.h" | ||
50 | #include "tree234.h" | ||
51 | |||
52 | /* | ||
53 | * To search for numbers we can make, we employ a breadth-first | ||
54 | * search across the space of sets of input numbers. That is, for | ||
55 | * example, we start with the set (3,6,25,50,75,100); we apply | ||
56 | * moves which involve combining two numbers (e.g. adding the 50 | ||
57 | * and the 75 takes us to the set (3,6,25,100,125); and then we see | ||
58 | * if we ever end up with a set containing (say) 952. | ||
59 | * | ||
60 | * If the rules are changed so that all the numbers must be used, | ||
61 | * this is easy to adjust to: we simply see if we end up with a set | ||
62 | * containing _only_ (say) 952. | ||
63 | * | ||
64 | * Obviously, we can vary the rules about permitted arithmetic | ||
65 | * operations simply by altering the set of valid moves in the bfs. | ||
66 | * However, there's one common rule in this sort of puzzle which | ||
67 | * takes a little more thought, and that's _concatenation_. For | ||
68 | * example, if you are given (say) four 4s and required to make 10, | ||
69 | * you are permitted to combine two of the 4s into a 44 to begin | ||
70 | * with, making (44-4)/4 = 10. However, you are generally not | ||
71 | * allowed to concatenate two numbers that _weren't_ both in the | ||
72 | * original input set (you couldn't multiply two 4s to get 16 and | ||
73 | * then concatenate a 4 on to it to make 164), so concatenation is | ||
74 | * not an operation which is valid in all situations. | ||
75 | * | ||
76 | * We could enforce this restriction by storing a flag alongside | ||
77 | * each number indicating whether or not it's an original number; | ||
78 | * the rules being that concatenation of two numbers is only valid | ||
79 | * if they both have the original flag, and that its output _also_ | ||
80 | * has the original flag (so that you can concatenate three 4s into | ||
81 | * a 444), but that applying any other arithmetic operation clears | ||
82 | * the original flag on the output. However, we can get marginally | ||
83 | * simpler than that by observing that since concatenation has to | ||
84 | * happen to a number before any other operation, we can simply | ||
85 | * place all the concatenations at the start of the search. In | ||
86 | * other words, we have a global flag on an entire number _set_ | ||
87 | * which indicates whether we are still permitted to perform | ||
88 | * concatenations; if so, we can concatenate any of the numbers in | ||
89 | * that set. Performing any other operation clears the flag. | ||
90 | */ | ||
91 | |||
92 | #define SETFLAG_CONCAT 1 /* we can do concatenation */ | ||
93 | |||
94 | struct sets; | ||
95 | |||
96 | struct ancestor { | ||
97 | struct set *prev; /* index of ancestor set in set list */ | ||
98 | unsigned char pa, pb, po, pr; /* operation that got here from prev */ | ||
99 | }; | ||
100 | |||
101 | struct set { | ||
102 | int *numbers; /* rationals stored as n,d pairs */ | ||
103 | short nnumbers; /* # of rationals, so half # of ints */ | ||
104 | short flags; /* SETFLAG_CONCAT only, at present */ | ||
105 | int npaths; /* number of ways to reach this set */ | ||
106 | struct ancestor a; /* primary ancestor */ | ||
107 | struct ancestor *as; /* further ancestors, if we care */ | ||
108 | int nas, assize; | ||
109 | }; | ||
110 | |||
111 | struct output { | ||
112 | int number; | ||
113 | struct set *set; | ||
114 | int index; /* which number in the set is it? */ | ||
115 | int npaths; /* number of ways to reach this */ | ||
116 | }; | ||
117 | |||
118 | #define SETLISTLEN 1024 | ||
119 | #define NUMBERLISTLEN 32768 | ||
120 | #define OUTPUTLISTLEN 1024 | ||
121 | struct operation; | ||
122 | struct sets { | ||
123 | struct set **setlists; | ||
124 | int nsets, nsetlists, setlistsize; | ||
125 | tree234 *settree; | ||
126 | int **numberlists; | ||
127 | int nnumbers, nnumberlists, numberlistsize; | ||
128 | struct output **outputlists; | ||
129 | int noutputs, noutputlists, outputlistsize; | ||
130 | tree234 *outputtree; | ||
131 | const struct operation *const *ops; | ||
132 | }; | ||
133 | |||
134 | #define OPFLAG_NEEDS_CONCAT 1 | ||
135 | #define OPFLAG_KEEPS_CONCAT 2 | ||
136 | #define OPFLAG_UNARY 4 | ||
137 | #define OPFLAG_UNARYPREFIX 8 | ||
138 | #define OPFLAG_FN 16 | ||
139 | |||
140 | struct operation { | ||
141 | /* | ||
142 | * Most operations should be shown in the output working, but | ||
143 | * concatenation should not; we just take the result of the | ||
144 | * concatenation and assume that it's obvious how it was | ||
145 | * derived. | ||
146 | */ | ||
147 | int display; | ||
148 | |||
149 | /* | ||
150 | * Text display of the operator, in expressions and for | ||
151 | * debugging respectively. | ||
152 | */ | ||
153 | const char *text, *dbgtext; | ||
154 | |||
155 | /* | ||
156 | * Flags dictating when the operator can be applied. | ||
157 | */ | ||
158 | int flags; | ||
159 | |||
160 | /* | ||
161 | * Priority of the operator (for avoiding unnecessary | ||
162 | * parentheses when formatting it into a string). | ||
163 | */ | ||
164 | int priority; | ||
165 | |||
166 | /* | ||
167 | * Associativity of the operator. Bit 0 means we need parens | ||
168 | * when the left operand of one of these operators is another | ||
169 | * instance of it, e.g. (2^3)^4. Bit 1 means we need parens | ||
170 | * when the right operand is another instance of the same | ||
171 | * operator, e.g. 2-(3-4). Thus: | ||
172 | * | ||
173 | * - this field is 0 for a fully associative operator, since | ||
174 | * we never need parens. | ||
175 | * - it's 1 for a right-associative operator. | ||
176 | * - it's 2 for a left-associative operator. | ||
177 | * - it's 3 for a _non_-associative operator (which always | ||
178 | * uses parens just to be sure). | ||
179 | */ | ||
180 | int assoc; | ||
181 | |||
182 | /* | ||
183 | * Whether the operator is commutative. Saves time in the | ||
184 | * search if we don't have to try it both ways round. | ||
185 | */ | ||
186 | int commutes; | ||
187 | |||
188 | /* | ||
189 | * Function which implements the operator. Returns true on | ||
190 | * success, false on failure. Takes two rationals and writes | ||
191 | * out a third. | ||
192 | */ | ||
193 | int (*perform)(int *a, int *b, int *output); | ||
194 | }; | ||
195 | |||
196 | struct rules { | ||
197 | const struct operation *const *ops; | ||
198 | int use_all; | ||
199 | }; | ||
200 | |||
201 | #define MUL(r, a, b) do { \ | ||
202 | (r) = (a) * (b); \ | ||
203 | if ((b) && (a) && (r) / (b) != (a)) return false; \ | ||
204 | } while (0) | ||
205 | |||
206 | #define ADD(r, a, b) do { \ | ||
207 | (r) = (a) + (b); \ | ||
208 | if ((a) > 0 && (b) > 0 && (r) < 0) return false; \ | ||
209 | if ((a) < 0 && (b) < 0 && (r) > 0) return false; \ | ||
210 | } while (0) | ||
211 | |||
212 | #define OUT(output, n, d) do { \ | ||
213 | int g = gcd((n),(d)); \ | ||
214 | if (g < 0) g = -g; \ | ||
215 | if ((d) < 0) g = -g; \ | ||
216 | if (g == -1 && (n) < -INT_MAX) return false; \ | ||
217 | if (g == -1 && (d) < -INT_MAX) return false; \ | ||
218 | (output)[0] = (n)/g; \ | ||
219 | (output)[1] = (d)/g; \ | ||
220 | assert((output)[1] > 0); \ | ||
221 | } while (0) | ||
222 | |||
223 | static int gcd(int x, int y) | ||
224 | { | ||
225 | while (x != 0 && y != 0) { | ||
226 | int t = x; | ||
227 | x = y; | ||
228 | y = t % y; | ||
229 | } | ||
230 | |||
231 | return abs(x + y); /* i.e. whichever one isn't zero */ | ||
232 | } | ||
233 | |||
234 | static int perform_add(int *a, int *b, int *output) | ||
235 | { | ||
236 | int at, bt, tn, bn; | ||
237 | /* | ||
238 | * a0/a1 + b0/b1 = (a0*b1 + b0*a1) / (a1*b1) | ||
239 | */ | ||
240 | MUL(at, a[0], b[1]); | ||
241 | MUL(bt, b[0], a[1]); | ||
242 | ADD(tn, at, bt); | ||
243 | MUL(bn, a[1], b[1]); | ||
244 | OUT(output, tn, bn); | ||
245 | return true; | ||
246 | } | ||
247 | |||
248 | static int perform_sub(int *a, int *b, int *output) | ||
249 | { | ||
250 | int at, bt, tn, bn; | ||
251 | /* | ||
252 | * a0/a1 - b0/b1 = (a0*b1 - b0*a1) / (a1*b1) | ||
253 | */ | ||
254 | MUL(at, a[0], b[1]); | ||
255 | MUL(bt, b[0], a[1]); | ||
256 | ADD(tn, at, -bt); | ||
257 | MUL(bn, a[1], b[1]); | ||
258 | OUT(output, tn, bn); | ||
259 | return true; | ||
260 | } | ||
261 | |||
262 | static int perform_mul(int *a, int *b, int *output) | ||
263 | { | ||
264 | int tn, bn; | ||
265 | /* | ||
266 | * a0/a1 * b0/b1 = (a0*b0) / (a1*b1) | ||
267 | */ | ||
268 | MUL(tn, a[0], b[0]); | ||
269 | MUL(bn, a[1], b[1]); | ||
270 | OUT(output, tn, bn); | ||
271 | return true; | ||
272 | } | ||
273 | |||
274 | static int perform_div(int *a, int *b, int *output) | ||
275 | { | ||
276 | int tn, bn; | ||
277 | |||
278 | /* | ||
279 | * Division by zero is outlawed. | ||
280 | */ | ||
281 | if (b[0] == 0) | ||
282 | return false; | ||
283 | |||
284 | /* | ||
285 | * a0/a1 / b0/b1 = (a0*b1) / (a1*b0) | ||
286 | */ | ||
287 | MUL(tn, a[0], b[1]); | ||
288 | MUL(bn, a[1], b[0]); | ||
289 | OUT(output, tn, bn); | ||
290 | return true; | ||
291 | } | ||
292 | |||
293 | static int perform_exact_div(int *a, int *b, int *output) | ||
294 | { | ||
295 | int tn, bn; | ||
296 | |||
297 | /* | ||
298 | * Division by zero is outlawed. | ||
299 | */ | ||
300 | if (b[0] == 0) | ||
301 | return false; | ||
302 | |||
303 | /* | ||
304 | * a0/a1 / b0/b1 = (a0*b1) / (a1*b0) | ||
305 | */ | ||
306 | MUL(tn, a[0], b[1]); | ||
307 | MUL(bn, a[1], b[0]); | ||
308 | OUT(output, tn, bn); | ||
309 | |||
310 | /* | ||
311 | * Exact division means we require the result to be an integer. | ||
312 | */ | ||
313 | return (output[1] == 1); | ||
314 | } | ||
315 | |||
316 | static int max_p10(int n, int *p10_r) | ||
317 | { | ||
318 | /* | ||
319 | * Find the smallest power of ten strictly greater than n. | ||
320 | * | ||
321 | * Special case: we must return at least 10, even if n is | ||
322 | * zero. (This is because this function is used for finding | ||
323 | * the power of ten by which to multiply a number being | ||
324 | * concatenated to the front of n, and concatenating 1 to 0 | ||
325 | * should yield 10 and not 1.) | ||
326 | */ | ||
327 | int p10 = 10; | ||
328 | while (p10 <= (INT_MAX/10) && p10 <= n) | ||
329 | p10 *= 10; | ||
330 | if (p10 > INT_MAX/10) | ||
331 | return false; /* integer overflow */ | ||
332 | *p10_r = p10; | ||
333 | return true; | ||
334 | } | ||
335 | |||
336 | static int perform_concat(int *a, int *b, int *output) | ||
337 | { | ||
338 | int t1, t2, p10; | ||
339 | |||
340 | /* | ||
341 | * We can't concatenate anything which isn't a non-negative | ||
342 | * integer. | ||
343 | */ | ||
344 | if (a[1] != 1 || b[1] != 1 || a[0] < 0 || b[0] < 0) | ||
345 | return false; | ||
346 | |||
347 | /* | ||
348 | * For concatenation, we can safely assume leading zeroes | ||
349 | * aren't an issue. It isn't clear whether they `should' be | ||
350 | * allowed, but it turns out not to matter: concatenating a | ||
351 | * leading zero on to a number in order to harmlessly get rid | ||
352 | * of the zero is never necessary because unwanted zeroes can | ||
353 | * be disposed of by adding them to something instead. So we | ||
354 | * disallow them always. | ||
355 | * | ||
356 | * The only other possibility is that you might want to | ||
357 | * concatenate a leading zero on to something and then | ||
358 | * concatenate another non-zero digit on to _that_ (to make, | ||
359 | * for example, 106); but that's also unnecessary, because you | ||
360 | * can make 106 just as easily by concatenating the 0 on to the | ||
361 | * _end_ of the 1 first. | ||
362 | */ | ||
363 | if (a[0] == 0) | ||
364 | return false; | ||
365 | |||
366 | if (!max_p10(b[0], &p10)) return false; | ||
367 | |||
368 | MUL(t1, p10, a[0]); | ||
369 | ADD(t2, t1, b[0]); | ||
370 | OUT(output, t2, 1); | ||
371 | return true; | ||
372 | } | ||
373 | |||
374 | #define IPOW(ret, x, y) do { \ | ||
375 | int ipow_limit = (y); \ | ||
376 | if ((x) == 1 || (x) == 0) ipow_limit = 1; \ | ||
377 | else if ((x) == -1) ipow_limit &= 1; \ | ||
378 | (ret) = 1; \ | ||
379 | while (ipow_limit-- > 0) { \ | ||
380 | int tmp; \ | ||
381 | MUL(tmp, ret, x); \ | ||
382 | ret = tmp; \ | ||
383 | } \ | ||
384 | } while (0) | ||
385 | |||
386 | static int perform_exp(int *a, int *b, int *output) | ||
387 | { | ||
388 | int an, ad, xn, xd; | ||
389 | |||
390 | /* | ||
391 | * Exponentiation is permitted if the result is rational. This | ||
392 | * means that: | ||
393 | * | ||
394 | * - first we see whether we can take the (denominator-of-b)th | ||
395 | * root of a and get a rational; if not, we give up. | ||
396 | * | ||
397 | * - then we do take that root of a | ||
398 | * | ||
399 | * - then we multiply by itself (numerator-of-b) times. | ||
400 | */ | ||
401 | if (b[1] > 1) { | ||
402 | an = (int)(0.5 + pow(a[0], 1.0/b[1])); | ||
403 | ad = (int)(0.5 + pow(a[1], 1.0/b[1])); | ||
404 | IPOW(xn, an, b[1]); | ||
405 | IPOW(xd, ad, b[1]); | ||
406 | if (xn != a[0] || xd != a[1]) | ||
407 | return false; | ||
408 | } else { | ||
409 | an = a[0]; | ||
410 | ad = a[1]; | ||
411 | } | ||
412 | if (b[0] >= 0) { | ||
413 | IPOW(xn, an, b[0]); | ||
414 | IPOW(xd, ad, b[0]); | ||
415 | } else { | ||
416 | IPOW(xd, an, -b[0]); | ||
417 | IPOW(xn, ad, -b[0]); | ||
418 | } | ||
419 | if (xd == 0) | ||
420 | return false; | ||
421 | |||
422 | OUT(output, xn, xd); | ||
423 | return true; | ||
424 | } | ||
425 | |||
426 | static int perform_factorial(int *a, int *b, int *output) | ||
427 | { | ||
428 | int ret, t, i; | ||
429 | |||
430 | /* | ||
431 | * Factorials of non-negative integers are permitted. | ||
432 | */ | ||
433 | if (a[1] != 1 || a[0] < 0) | ||
434 | return false; | ||
435 | |||
436 | /* | ||
437 | * However, a special case: we don't take a factorial of | ||
438 | * anything which would thereby remain the same. | ||
439 | */ | ||
440 | if (a[0] == 1 || a[0] == 2) | ||
441 | return false; | ||
442 | |||
443 | ret = 1; | ||
444 | for (i = 1; i <= a[0]; i++) { | ||
445 | MUL(t, ret, i); | ||
446 | ret = t; | ||
447 | } | ||
448 | |||
449 | OUT(output, ret, 1); | ||
450 | return true; | ||
451 | } | ||
452 | |||
453 | static int perform_decimal(int *a, int *b, int *output) | ||
454 | { | ||
455 | int p10; | ||
456 | |||
457 | /* | ||
458 | * Add a decimal digit to the front of a number; | ||
459 | * fail if it's not an integer. | ||
460 | * So, 1 --> 0.1, 15 --> 0.15, | ||
461 | * or, rather, 1 --> 1/10, 15 --> 15/100, | ||
462 | * x --> x / (smallest power of 10 > than x) | ||
463 | * | ||
464 | */ | ||
465 | if (a[1] != 1) return false; | ||
466 | |||
467 | if (!max_p10(a[0], &p10)) return false; | ||
468 | |||
469 | OUT(output, a[0], p10); | ||
470 | return true; | ||
471 | } | ||
472 | |||
473 | static int perform_recur(int *a, int *b, int *output) | ||
474 | { | ||
475 | int p10, tn, bn; | ||
476 | |||
477 | /* | ||
478 | * This converts a number like .4 to .44444..., or .45 to .45454... | ||
479 | * The input number must be -1 < a < 1. | ||
480 | * | ||
481 | * Calculate the smallest power of 10 that divides the denominator exactly, | ||
482 | * returning if no such power of 10 exists. Then multiply the numerator | ||
483 | * up accordingly, and the new denominator becomes that power of 10 - 1. | ||
484 | */ | ||
485 | if (abs(a[0]) >= abs(a[1])) return false; /* -1 < a < 1 */ | ||
486 | |||
487 | p10 = 10; | ||
488 | while (p10 <= (INT_MAX/10)) { | ||
489 | if ((a[1] <= p10) && (p10 % a[1]) == 0) goto found; | ||
490 | p10 *= 10; | ||
491 | } | ||
492 | return false; | ||
493 | found: | ||
494 | tn = a[0] * (p10 / a[1]); | ||
495 | bn = p10 - 1; | ||
496 | |||
497 | OUT(output, tn, bn); | ||
498 | return true; | ||
499 | } | ||
500 | |||
501 | static int perform_root(int *a, int *b, int *output) | ||
502 | { | ||
503 | /* | ||
504 | * A root B is: 1 iff a == 0 | ||
505 | * B ^ (1/A) otherwise | ||
506 | */ | ||
507 | int ainv[2], res; | ||
508 | |||
509 | if (a[0] == 0) { | ||
510 | OUT(output, 1, 1); | ||
511 | return true; | ||
512 | } | ||
513 | |||
514 | OUT(ainv, a[1], a[0]); | ||
515 | res = perform_exp(b, ainv, output); | ||
516 | return res; | ||
517 | } | ||
518 | |||
519 | static int perform_perc(int *a, int *b, int *output) | ||
520 | { | ||
521 | if (a[0] == 0) return false; /* 0% = 0, uninteresting. */ | ||
522 | if (a[1] > (INT_MAX/100)) return false; | ||
523 | |||
524 | OUT(output, a[0], a[1]*100); | ||
525 | return true; | ||
526 | } | ||
527 | |||
528 | static int perform_gamma(int *a, int *b, int *output) | ||
529 | { | ||
530 | int asub1[2]; | ||
531 | |||
532 | /* | ||
533 | * gamma(a) = (a-1)! | ||
534 | * | ||
535 | * special case not caught by perform_fact: gamma(1) is 1 so | ||
536 | * don't bother. | ||
537 | */ | ||
538 | if (a[0] == 1 && a[1] == 1) return false; | ||
539 | |||
540 | OUT(asub1, a[0]-a[1], a[1]); | ||
541 | return perform_factorial(asub1, b, output); | ||
542 | } | ||
543 | |||
544 | static int perform_sqrt(int *a, int *b, int *output) | ||
545 | { | ||
546 | int half[2] = { 1, 2 }; | ||
547 | |||
548 | /* | ||
549 | * sqrt(0) == 0, sqrt(1) == 1: don't perform unary noops. | ||
550 | */ | ||
551 | if (a[0] == 0 || (a[0] == 1 && a[1] == 1)) return false; | ||
552 | |||
553 | return perform_exp(a, half, output); | ||
554 | } | ||
555 | |||
556 | static const struct operation op_add = { | ||
557 | true, "+", "+", 0, 10, 0, true, perform_add | ||
558 | }; | ||
559 | static const struct operation op_sub = { | ||
560 | true, "-", "-", 0, 10, 2, false, perform_sub | ||
561 | }; | ||
562 | static const struct operation op_mul = { | ||
563 | true, "*", "*", 0, 20, 0, true, perform_mul | ||
564 | }; | ||
565 | static const struct operation op_div = { | ||
566 | true, "/", "/", 0, 20, 2, false, perform_div | ||
567 | }; | ||
568 | static const struct operation op_xdiv = { | ||
569 | true, "/", "/", 0, 20, 2, false, perform_exact_div | ||
570 | }; | ||
571 | static const struct operation op_concat = { | ||
572 | false, "", "concat", OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT, | ||
573 | 1000, 0, false, perform_concat | ||
574 | }; | ||
575 | static const struct operation op_exp = { | ||
576 | true, "^", "^", 0, 30, 1, false, perform_exp | ||
577 | }; | ||
578 | static const struct operation op_factorial = { | ||
579 | true, "!", "!", OPFLAG_UNARY, 40, 0, false, perform_factorial | ||
580 | }; | ||
581 | static const struct operation op_decimal = { | ||
582 | true, ".", ".", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT, 50, 0, false, perform_decimal | ||
583 | }; | ||
584 | static const struct operation op_recur = { | ||
585 | true, "...", "recur", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 2, false, perform_recur | ||
586 | }; | ||
587 | static const struct operation op_root = { | ||
588 | true, "v~", "root", 0, 30, 1, false, perform_root | ||
589 | }; | ||
590 | static const struct operation op_perc = { | ||
591 | true, "%", "%", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 1, false, perform_perc | ||
592 | }; | ||
593 | static const struct operation op_gamma = { | ||
594 | true, "gamma", "gamma", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_FN, 1, 3, false, perform_gamma | ||
595 | }; | ||
596 | static const struct operation op_sqrt = { | ||
597 | true, "v~", "sqrt", OPFLAG_UNARY | OPFLAG_UNARYPREFIX, 30, 1, false, perform_sqrt | ||
598 | }; | ||
599 | |||
600 | /* | ||
601 | * In Countdown, divisions resulting in fractions are disallowed. | ||
602 | * http://www.askoxford.com/wordgames/countdown/rules/ | ||
603 | */ | ||
604 | static const struct operation *const ops_countdown[] = { | ||
605 | &op_add, &op_mul, &op_sub, &op_xdiv, NULL | ||
606 | }; | ||
607 | static const struct rules rules_countdown = { | ||
608 | ops_countdown, false | ||
609 | }; | ||
610 | |||
611 | /* | ||
612 | * A slightly different rule set which handles the reasonably well | ||
613 | * known puzzle of making 24 using two 3s and two 8s. For this we | ||
614 | * need rational rather than integer division. | ||
615 | */ | ||
616 | static const struct operation *const ops_3388[] = { | ||
617 | &op_add, &op_mul, &op_sub, &op_div, NULL | ||
618 | }; | ||
619 | static const struct rules rules_3388 = { | ||
620 | ops_3388, true | ||
621 | }; | ||
622 | |||
623 | /* | ||
624 | * A still more permissive rule set usable for the four-4s problem | ||
625 | * and similar things. Permits concatenation. | ||
626 | */ | ||
627 | static const struct operation *const ops_four4s[] = { | ||
628 | &op_add, &op_mul, &op_sub, &op_div, &op_concat, NULL | ||
629 | }; | ||
630 | static const struct rules rules_four4s = { | ||
631 | ops_four4s, true | ||
632 | }; | ||
633 | |||
634 | /* | ||
635 | * The most permissive ruleset I can think of. Permits | ||
636 | * exponentiation, and also silly unary operators like factorials. | ||
637 | */ | ||
638 | static const struct operation *const ops_anythinggoes[] = { | ||
639 | &op_add, &op_mul, &op_sub, &op_div, &op_concat, &op_exp, &op_factorial, | ||
640 | &op_decimal, &op_recur, &op_root, &op_perc, &op_gamma, &op_sqrt, NULL | ||
641 | }; | ||
642 | static const struct rules rules_anythinggoes = { | ||
643 | ops_anythinggoes, true | ||
644 | }; | ||
645 | |||
646 | #define ratcmp(a,op,b) ( (long long)(a)[0] * (b)[1] op \ | ||
647 | (long long)(b)[0] * (a)[1] ) | ||
648 | |||
649 | static int addtoset(struct set *set, int newnumber[2]) | ||
650 | { | ||
651 | int i, j; | ||
652 | |||
653 | /* Find where we want to insert the new number */ | ||
654 | for (i = 0; i < set->nnumbers && | ||
655 | ratcmp(set->numbers+2*i, <, newnumber); i++); | ||
656 | |||
657 | /* Move everything else up */ | ||
658 | for (j = set->nnumbers; j > i; j--) { | ||
659 | set->numbers[2*j] = set->numbers[2*j-2]; | ||
660 | set->numbers[2*j+1] = set->numbers[2*j-1]; | ||
661 | } | ||
662 | |||
663 | /* Insert the new number */ | ||
664 | set->numbers[2*i] = newnumber[0]; | ||
665 | set->numbers[2*i+1] = newnumber[1]; | ||
666 | |||
667 | set->nnumbers++; | ||
668 | |||
669 | return i; | ||
670 | } | ||
671 | |||
672 | #define ensure(array, size, newlen, type) do { \ | ||
673 | if ((newlen) > (size)) { \ | ||
674 | (size) = (newlen) + 512; \ | ||
675 | (array) = sresize((array), (size), type); \ | ||
676 | } \ | ||
677 | } while (0) | ||
678 | |||
679 | static int setcmp(void *av, void *bv) | ||
680 | { | ||
681 | struct set *a = (struct set *)av; | ||
682 | struct set *b = (struct set *)bv; | ||
683 | int i; | ||
684 | |||
685 | if (a->nnumbers < b->nnumbers) | ||
686 | return -1; | ||
687 | else if (a->nnumbers > b->nnumbers) | ||
688 | return +1; | ||
689 | |||
690 | if (a->flags < b->flags) | ||
691 | return -1; | ||
692 | else if (a->flags > b->flags) | ||
693 | return +1; | ||
694 | |||
695 | for (i = 0; i < a->nnumbers; i++) { | ||
696 | if (ratcmp(a->numbers+2*i, <, b->numbers+2*i)) | ||
697 | return -1; | ||
698 | else if (ratcmp(a->numbers+2*i, >, b->numbers+2*i)) | ||
699 | return +1; | ||
700 | } | ||
701 | |||
702 | return 0; | ||
703 | } | ||
704 | |||
705 | static int outputcmp(void *av, void *bv) | ||
706 | { | ||
707 | struct output *a = (struct output *)av; | ||
708 | struct output *b = (struct output *)bv; | ||
709 | |||
710 | if (a->number < b->number) | ||
711 | return -1; | ||
712 | else if (a->number > b->number) | ||
713 | return +1; | ||
714 | |||
715 | return 0; | ||
716 | } | ||
717 | |||
718 | static int outputfindcmp(void *av, void *bv) | ||
719 | { | ||
720 | int *a = (int *)av; | ||
721 | struct output *b = (struct output *)bv; | ||
722 | |||
723 | if (*a < b->number) | ||
724 | return -1; | ||
725 | else if (*a > b->number) | ||
726 | return +1; | ||
727 | |||
728 | return 0; | ||
729 | } | ||
730 | |||
731 | static void addset(struct sets *s, struct set *set, int multiple, | ||
732 | struct set *prev, int pa, int po, int pb, int pr) | ||
733 | { | ||
734 | struct set *s2; | ||
735 | int npaths = (prev ? prev->npaths : 1); | ||
736 | |||
737 | assert(set == s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN); | ||
738 | s2 = add234(s->settree, set); | ||
739 | if (s2 == set) { | ||
740 | /* | ||
741 | * New set added to the tree. | ||
742 | */ | ||
743 | set->a.prev = prev; | ||
744 | set->a.pa = pa; | ||
745 | set->a.po = po; | ||
746 | set->a.pb = pb; | ||
747 | set->a.pr = pr; | ||
748 | set->npaths = npaths; | ||
749 | s->nsets++; | ||
750 | s->nnumbers += 2 * set->nnumbers; | ||
751 | set->as = NULL; | ||
752 | set->nas = set->assize = 0; | ||
753 | } else { | ||
754 | /* | ||
755 | * Rediscovered an existing set. Update its npaths. | ||
756 | */ | ||
757 | s2->npaths += npaths; | ||
758 | /* | ||
759 | * And optionally enter it as an additional ancestor. | ||
760 | */ | ||
761 | if (multiple) { | ||
762 | if (s2->nas >= s2->assize) { | ||
763 | s2->assize = s2->nas * 3 / 2 + 4; | ||
764 | s2->as = sresize(s2->as, s2->assize, struct ancestor); | ||
765 | } | ||
766 | s2->as[s2->nas].prev = prev; | ||
767 | s2->as[s2->nas].pa = pa; | ||
768 | s2->as[s2->nas].po = po; | ||
769 | s2->as[s2->nas].pb = pb; | ||
770 | s2->as[s2->nas].pr = pr; | ||
771 | s2->nas++; | ||
772 | } | ||
773 | } | ||
774 | } | ||
775 | |||
776 | static struct set *newset(struct sets *s, int nnumbers, int flags) | ||
777 | { | ||
778 | struct set *sn; | ||
779 | |||
780 | ensure(s->setlists, s->setlistsize, s->nsets/SETLISTLEN+1, struct set *); | ||
781 | while (s->nsetlists <= s->nsets / SETLISTLEN) | ||
782 | s->setlists[s->nsetlists++] = snewn(SETLISTLEN, struct set); | ||
783 | sn = s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN; | ||
784 | |||
785 | if (s->nnumbers + nnumbers * 2 > s->nnumberlists * NUMBERLISTLEN) | ||
786 | s->nnumbers = s->nnumberlists * NUMBERLISTLEN; | ||
787 | ensure(s->numberlists, s->numberlistsize, | ||
788 | s->nnumbers/NUMBERLISTLEN+1, int *); | ||
789 | while (s->nnumberlists <= s->nnumbers / NUMBERLISTLEN) | ||
790 | s->numberlists[s->nnumberlists++] = snewn(NUMBERLISTLEN, int); | ||
791 | sn->numbers = s->numberlists[s->nnumbers / NUMBERLISTLEN] + | ||
792 | s->nnumbers % NUMBERLISTLEN; | ||
793 | |||
794 | /* | ||
795 | * Start the set off empty. | ||
796 | */ | ||
797 | sn->nnumbers = 0; | ||
798 | |||
799 | sn->flags = flags; | ||
800 | |||
801 | return sn; | ||
802 | } | ||
803 | |||
804 | static int addoutput(struct sets *s, struct set *ss, int index, int *n) | ||
805 | { | ||
806 | struct output *o, *o2; | ||
807 | |||
808 | /* | ||
809 | * Target numbers are always integers. | ||
810 | */ | ||
811 | if (ss->numbers[2*index+1] != 1) | ||
812 | return false; | ||
813 | |||
814 | ensure(s->outputlists, s->outputlistsize, s->noutputs/OUTPUTLISTLEN+1, | ||
815 | struct output *); | ||
816 | while (s->noutputlists <= s->noutputs / OUTPUTLISTLEN) | ||
817 | s->outputlists[s->noutputlists++] = snewn(OUTPUTLISTLEN, | ||
818 | struct output); | ||
819 | o = s->outputlists[s->noutputs / OUTPUTLISTLEN] + | ||
820 | s->noutputs % OUTPUTLISTLEN; | ||
821 | |||
822 | o->number = ss->numbers[2*index]; | ||
823 | o->set = ss; | ||
824 | o->index = index; | ||
825 | o->npaths = ss->npaths; | ||
826 | o2 = add234(s->outputtree, o); | ||
827 | if (o2 != o) { | ||
828 | o2->npaths += o->npaths; | ||
829 | } else { | ||
830 | s->noutputs++; | ||
831 | } | ||
832 | *n = o->number; | ||
833 | return true; | ||
834 | } | ||
835 | |||
836 | static struct sets *do_search(int ninputs, int *inputs, | ||
837 | const struct rules *rules, int *target, | ||
838 | int debug, int multiple) | ||
839 | { | ||
840 | struct sets *s; | ||
841 | struct set *sn; | ||
842 | int qpos, i; | ||
843 | const struct operation *const *ops = rules->ops; | ||
844 | |||
845 | s = snew(struct sets); | ||
846 | s->setlists = NULL; | ||
847 | s->nsets = s->nsetlists = s->setlistsize = 0; | ||
848 | s->numberlists = NULL; | ||
849 | s->nnumbers = s->nnumberlists = s->numberlistsize = 0; | ||
850 | s->outputlists = NULL; | ||
851 | s->noutputs = s->noutputlists = s->outputlistsize = 0; | ||
852 | s->settree = newtree234(setcmp); | ||
853 | s->outputtree = newtree234(outputcmp); | ||
854 | s->ops = ops; | ||
855 | |||
856 | /* | ||
857 | * Start with the input set. | ||
858 | */ | ||
859 | sn = newset(s, ninputs, SETFLAG_CONCAT); | ||
860 | for (i = 0; i < ninputs; i++) { | ||
861 | int newnumber[2]; | ||
862 | newnumber[0] = inputs[i]; | ||
863 | newnumber[1] = 1; | ||
864 | addtoset(sn, newnumber); | ||
865 | } | ||
866 | addset(s, sn, multiple, NULL, 0, 0, 0, 0); | ||
867 | |||
868 | /* | ||
869 | * Now perform the breadth-first search: keep looping over sets | ||
870 | * until we run out of steam. | ||
871 | */ | ||
872 | qpos = 0; | ||
873 | while (qpos < s->nsets) { | ||
874 | struct set *ss = s->setlists[qpos / SETLISTLEN] + qpos % SETLISTLEN; | ||
875 | struct set *sn; | ||
876 | int i, j, k, m; | ||
877 | |||
878 | if (debug) { | ||
879 | int i; | ||
880 | printf("processing set:"); | ||
881 | for (i = 0; i < ss->nnumbers; i++) { | ||
882 | printf(" %d", ss->numbers[2*i]); | ||
883 | if (ss->numbers[2*i+1] != 1) | ||
884 | printf("/%d", ss->numbers[2*i+1]); | ||
885 | } | ||
886 | printf("\n"); | ||
887 | } | ||
888 | |||
889 | /* | ||
890 | * Record all the valid output numbers in this state. We | ||
891 | * can always do this if there's only one number in the | ||
892 | * state; otherwise, we can only do it if we aren't | ||
893 | * required to use all the numbers in coming to our answer. | ||
894 | */ | ||
895 | if (ss->nnumbers == 1 || !rules->use_all) { | ||
896 | for (i = 0; i < ss->nnumbers; i++) { | ||
897 | int n; | ||
898 | |||
899 | if (addoutput(s, ss, i, &n) && target && n == *target) | ||
900 | return s; | ||
901 | } | ||
902 | } | ||
903 | |||
904 | /* | ||
905 | * Try every possible operation from this state. | ||
906 | */ | ||
907 | for (k = 0; ops[k] && ops[k]->perform; k++) { | ||
908 | if ((ops[k]->flags & OPFLAG_NEEDS_CONCAT) && | ||
909 | !(ss->flags & SETFLAG_CONCAT)) | ||
910 | continue; /* can't use this operation here */ | ||
911 | for (i = 0; i < ss->nnumbers; i++) { | ||
912 | int jlimit = (ops[k]->flags & OPFLAG_UNARY ? 1 : ss->nnumbers); | ||
913 | for (j = 0; j < jlimit; j++) { | ||
914 | int n[2], newnn = ss->nnumbers; | ||
915 | int pa, po, pb, pr; | ||
916 | |||
917 | if (!(ops[k]->flags & OPFLAG_UNARY)) { | ||
918 | if (i == j) | ||
919 | continue; /* can't combine a number with itself */ | ||
920 | if (i > j && ops[k]->commutes) | ||
921 | continue; /* no need to do this both ways round */ | ||
922 | newnn--; | ||
923 | } | ||
924 | if (!ops[k]->perform(ss->numbers+2*i, ss->numbers+2*j, n)) | ||
925 | continue; /* operation failed */ | ||
926 | |||
927 | sn = newset(s, newnn, ss->flags); | ||
928 | |||
929 | if (!(ops[k]->flags & OPFLAG_KEEPS_CONCAT)) | ||
930 | sn->flags &= ~SETFLAG_CONCAT; | ||
931 | |||
932 | for (m = 0; m < ss->nnumbers; m++) { | ||
933 | if (m == i || (!(ops[k]->flags & OPFLAG_UNARY) && | ||
934 | m == j)) | ||
935 | continue; | ||
936 | sn->numbers[2*sn->nnumbers] = ss->numbers[2*m]; | ||
937 | sn->numbers[2*sn->nnumbers + 1] = ss->numbers[2*m + 1]; | ||
938 | sn->nnumbers++; | ||
939 | } | ||
940 | pa = i; | ||
941 | if (ops[k]->flags & OPFLAG_UNARY) | ||
942 | pb = sn->nnumbers+10; | ||
943 | else | ||
944 | pb = j; | ||
945 | po = k; | ||
946 | pr = addtoset(sn, n); | ||
947 | addset(s, sn, multiple, ss, pa, po, pb, pr); | ||
948 | if (debug) { | ||
949 | int i; | ||
950 | if (ops[k]->flags & OPFLAG_UNARYPREFIX) | ||
951 | printf(" %s %d ->", ops[po]->dbgtext, pa); | ||
952 | else if (ops[k]->flags & OPFLAG_UNARY) | ||
953 | printf(" %d %s ->", pa, ops[po]->dbgtext); | ||
954 | else | ||
955 | printf(" %d %s %d ->", pa, ops[po]->dbgtext, pb); | ||
956 | for (i = 0; i < sn->nnumbers; i++) { | ||
957 | printf(" %d", sn->numbers[2*i]); | ||
958 | if (sn->numbers[2*i+1] != 1) | ||
959 | printf("/%d", sn->numbers[2*i+1]); | ||
960 | } | ||
961 | printf("\n"); | ||
962 | } | ||
963 | } | ||
964 | } | ||
965 | } | ||
966 | |||
967 | qpos++; | ||
968 | } | ||
969 | |||
970 | return s; | ||
971 | } | ||
972 | |||
973 | static void free_sets(struct sets *s) | ||
974 | { | ||
975 | int i; | ||
976 | |||
977 | freetree234(s->settree); | ||
978 | freetree234(s->outputtree); | ||
979 | for (i = 0; i < s->nsetlists; i++) | ||
980 | sfree(s->setlists[i]); | ||
981 | sfree(s->setlists); | ||
982 | for (i = 0; i < s->nnumberlists; i++) | ||
983 | sfree(s->numberlists[i]); | ||
984 | sfree(s->numberlists); | ||
985 | for (i = 0; i < s->noutputlists; i++) | ||
986 | sfree(s->outputlists[i]); | ||
987 | sfree(s->outputlists); | ||
988 | sfree(s); | ||
989 | } | ||
990 | |||
991 | /* | ||
992 | * Print a text formula for producing a given output. | ||
993 | */ | ||
994 | static void print_recurse(struct sets *s, struct set *ss, int pathindex, | ||
995 | int index, int priority, int assoc, int child); | ||
996 | static void print_recurse_inner(struct sets *s, struct set *ss, | ||
997 | struct ancestor *a, int pathindex, int index, | ||
998 | int priority, int assoc, int child) | ||
999 | { | ||
1000 | if (a->prev && index != a->pr) { | ||
1001 | int pi; | ||
1002 | |||
1003 | /* | ||
1004 | * This number was passed straight down from this set's | ||
1005 | * predecessor. Find its index in the previous set and | ||
1006 | * recurse to there. | ||
1007 | */ | ||
1008 | pi = index; | ||
1009 | assert(pi != a->pr); | ||
1010 | if (pi > a->pr) | ||
1011 | pi--; | ||
1012 | if (pi >= min(a->pa, a->pb)) { | ||
1013 | pi++; | ||
1014 | if (pi >= max(a->pa, a->pb)) | ||
1015 | pi++; | ||
1016 | } | ||
1017 | print_recurse(s, a->prev, pathindex, pi, priority, assoc, child); | ||
1018 | } else if (a->prev && index == a->pr && | ||
1019 | s->ops[a->po]->display) { | ||
1020 | /* | ||
1021 | * This number was created by a displayed operator in the | ||
1022 | * transition from this set to its predecessor. Hence we | ||
1023 | * write an open paren, then recurse into the first | ||
1024 | * operand, then write the operator, then the second | ||
1025 | * operand, and finally close the paren. | ||
1026 | */ | ||
1027 | const char *op; | ||
1028 | int parens, thispri, thisassoc; | ||
1029 | |||
1030 | /* | ||
1031 | * Determine whether we need parentheses. | ||
1032 | */ | ||
1033 | thispri = s->ops[a->po]->priority; | ||
1034 | thisassoc = s->ops[a->po]->assoc; | ||
1035 | parens = (thispri < priority || | ||
1036 | (thispri == priority && (assoc & child))); | ||
1037 | |||
1038 | if (parens) | ||
1039 | putchar('('); | ||
1040 | |||
1041 | if (s->ops[a->po]->flags & OPFLAG_UNARYPREFIX) | ||
1042 | for (op = s->ops[a->po]->text; *op; op++) | ||
1043 | putchar(*op); | ||
1044 | |||
1045 | if (s->ops[a->po]->flags & OPFLAG_FN) | ||
1046 | putchar('('); | ||
1047 | |||
1048 | print_recurse(s, a->prev, pathindex, a->pa, thispri, thisassoc, 1); | ||
1049 | |||
1050 | if (s->ops[a->po]->flags & OPFLAG_FN) | ||
1051 | putchar(')'); | ||
1052 | |||
1053 | if (!(s->ops[a->po]->flags & OPFLAG_UNARYPREFIX)) | ||
1054 | for (op = s->ops[a->po]->text; *op; op++) | ||
1055 | putchar(*op); | ||
1056 | |||
1057 | if (!(s->ops[a->po]->flags & OPFLAG_UNARY)) | ||
1058 | print_recurse(s, a->prev, pathindex, a->pb, thispri, thisassoc, 2); | ||
1059 | |||
1060 | if (parens) | ||
1061 | putchar(')'); | ||
1062 | } else { | ||
1063 | /* | ||
1064 | * This number is either an original, or something formed | ||
1065 | * by a non-displayed operator (concatenation). Either way, | ||
1066 | * we display it as is. | ||
1067 | */ | ||
1068 | printf("%d", ss->numbers[2*index]); | ||
1069 | if (ss->numbers[2*index+1] != 1) | ||
1070 | printf("/%d", ss->numbers[2*index+1]); | ||
1071 | } | ||
1072 | } | ||
1073 | static void print_recurse(struct sets *s, struct set *ss, int pathindex, | ||
1074 | int index, int priority, int assoc, int child) | ||
1075 | { | ||
1076 | if (!ss->a.prev || pathindex < ss->a.prev->npaths) { | ||
1077 | print_recurse_inner(s, ss, &ss->a, pathindex, | ||
1078 | index, priority, assoc, child); | ||
1079 | } else { | ||
1080 | int i; | ||
1081 | pathindex -= ss->a.prev->npaths; | ||
1082 | for (i = 0; i < ss->nas; i++) { | ||
1083 | if (pathindex < ss->as[i].prev->npaths) { | ||
1084 | print_recurse_inner(s, ss, &ss->as[i], pathindex, | ||
1085 | index, priority, assoc, child); | ||
1086 | break; | ||
1087 | } | ||
1088 | pathindex -= ss->as[i].prev->npaths; | ||
1089 | } | ||
1090 | } | ||
1091 | } | ||
1092 | static void print(int pathindex, struct sets *s, struct output *o) | ||
1093 | { | ||
1094 | print_recurse(s, o->set, pathindex, o->index, 0, 0, 0); | ||
1095 | } | ||
1096 | |||
1097 | /* | ||
1098 | * gcc -g -O0 -o numgame numgame.c -I.. ../{malloc,tree234,nullfe}.c -lm | ||
1099 | */ | ||
1100 | int main(int argc, char **argv) | ||
1101 | { | ||
1102 | int doing_opts = true; | ||
1103 | const struct rules *rules = NULL; | ||
1104 | char *pname = argv[0]; | ||
1105 | int got_target = false, target = 0; | ||
1106 | int numbers[10], nnumbers = 0; | ||
1107 | int verbose = false; | ||
1108 | int pathcounts = false; | ||
1109 | int multiple = false; | ||
1110 | int debug_bfs = false; | ||
1111 | int got_range = false, rangemin = 0, rangemax = 0; | ||
1112 | |||
1113 | struct output *o; | ||
1114 | struct sets *s; | ||
1115 | int i, start, limit; | ||
1116 | |||
1117 | while (--argc) { | ||
1118 | char *p = *++argv; | ||
1119 | int c; | ||
1120 | |||
1121 | if (doing_opts && *p == '-') { | ||
1122 | p++; | ||
1123 | |||
1124 | if (!strcmp(p, "-")) { | ||
1125 | doing_opts = false; | ||
1126 | continue; | ||
1127 | } else if (*p == '-') { | ||
1128 | p++; | ||
1129 | if (!strcmp(p, "debug-bfs")) { | ||
1130 | debug_bfs = true; | ||
1131 | } else { | ||
1132 | fprintf(stderr, "%s: option '--%s' not recognised\n", | ||
1133 | pname, p); | ||
1134 | } | ||
1135 | } else while (p && *p) switch (c = *p++) { | ||
1136 | case 'C': | ||
1137 | rules = &rules_countdown; | ||
1138 | break; | ||
1139 | case 'B': | ||
1140 | rules = &rules_3388; | ||
1141 | break; | ||
1142 | case 'D': | ||
1143 | rules = &rules_four4s; | ||
1144 | break; | ||
1145 | case 'A': | ||
1146 | rules = &rules_anythinggoes; | ||
1147 | break; | ||
1148 | case 'v': | ||
1149 | verbose = true; | ||
1150 | break; | ||
1151 | case 'p': | ||
1152 | pathcounts = true; | ||
1153 | break; | ||
1154 | case 'm': | ||
1155 | multiple = true; | ||
1156 | break; | ||
1157 | case 't': | ||
1158 | case 'r': | ||
1159 | { | ||
1160 | char *v; | ||
1161 | if (*p) { | ||
1162 | v = p; | ||
1163 | p = NULL; | ||
1164 | } else if (--argc) { | ||
1165 | v = *++argv; | ||
1166 | } else { | ||
1167 | fprintf(stderr, "%s: option '-%c' expects an" | ||
1168 | " argument\n", pname, c); | ||
1169 | return 1; | ||
1170 | } | ||
1171 | switch (c) { | ||
1172 | case 't': | ||
1173 | got_target = true; | ||
1174 | target = atoi(v); | ||
1175 | break; | ||
1176 | case 'r': | ||
1177 | { | ||
1178 | char *sep = strchr(v, '-'); | ||
1179 | got_range = true; | ||
1180 | if (sep) { | ||
1181 | rangemin = atoi(v); | ||
1182 | rangemax = atoi(sep+1); | ||
1183 | } else { | ||
1184 | rangemin = 0; | ||
1185 | rangemax = atoi(v); | ||
1186 | } | ||
1187 | } | ||
1188 | break; | ||
1189 | } | ||
1190 | } | ||
1191 | break; | ||
1192 | default: | ||
1193 | fprintf(stderr, "%s: option '-%c' not" | ||
1194 | " recognised\n", pname, c); | ||
1195 | return 1; | ||
1196 | } | ||
1197 | } else { | ||
1198 | if (nnumbers >= lenof(numbers)) { | ||
1199 | fprintf(stderr, "%s: internal limit of %d numbers exceeded\n", | ||
1200 | pname, (int)lenof(numbers)); | ||
1201 | return 1; | ||
1202 | } else { | ||
1203 | numbers[nnumbers++] = atoi(p); | ||
1204 | } | ||
1205 | } | ||
1206 | } | ||
1207 | |||
1208 | if (!rules) { | ||
1209 | fprintf(stderr, "%s: no rule set specified; use -C,-B,-D,-A\n", pname); | ||
1210 | return 1; | ||
1211 | } | ||
1212 | |||
1213 | if (!nnumbers) { | ||
1214 | fprintf(stderr, "%s: no input numbers specified\n", pname); | ||
1215 | return 1; | ||
1216 | } | ||
1217 | |||
1218 | if (got_range) { | ||
1219 | if (got_target) { | ||
1220 | fprintf(stderr, "%s: only one of -t and -r may be specified\n", pname); | ||
1221 | return 1; | ||
1222 | } | ||
1223 | if (rangemin >= rangemax) { | ||
1224 | fprintf(stderr, "%s: range not sensible (%d - %d)\n", pname, rangemin, rangemax); | ||
1225 | return 1; | ||
1226 | } | ||
1227 | } | ||
1228 | |||
1229 | s = do_search(nnumbers, numbers, rules, (got_target ? &target : NULL), | ||
1230 | debug_bfs, multiple); | ||
1231 | |||
1232 | if (got_target) { | ||
1233 | o = findrelpos234(s->outputtree, &target, outputfindcmp, | ||
1234 | REL234_LE, &start); | ||
1235 | if (!o) | ||
1236 | start = -1; | ||
1237 | o = findrelpos234(s->outputtree, &target, outputfindcmp, | ||
1238 | REL234_GE, &limit); | ||
1239 | if (!o) | ||
1240 | limit = -1; | ||
1241 | assert(start != -1 || limit != -1); | ||
1242 | if (start == -1) | ||
1243 | start = limit; | ||
1244 | else if (limit == -1) | ||
1245 | limit = start; | ||
1246 | limit++; | ||
1247 | } else if (got_range) { | ||
1248 | if (!findrelpos234(s->outputtree, &rangemin, outputfindcmp, | ||
1249 | REL234_GE, &start) || | ||
1250 | !findrelpos234(s->outputtree, &rangemax, outputfindcmp, | ||
1251 | REL234_LE, &limit)) { | ||
1252 | printf("No solutions available in specified range %d-%d\n", rangemin, rangemax); | ||
1253 | return 1; | ||
1254 | } | ||
1255 | limit++; | ||
1256 | } else { | ||
1257 | start = 0; | ||
1258 | limit = count234(s->outputtree); | ||
1259 | } | ||
1260 | |||
1261 | for (i = start; i < limit; i++) { | ||
1262 | char buf[256]; | ||
1263 | |||
1264 | o = index234(s->outputtree, i); | ||
1265 | |||
1266 | sprintf(buf, "%d", o->number); | ||
1267 | |||
1268 | if (pathcounts) | ||
1269 | sprintf(buf + strlen(buf), " [%d]", o->npaths); | ||
1270 | |||
1271 | if (got_target || verbose) { | ||
1272 | int j, npaths; | ||
1273 | |||
1274 | if (multiple) | ||
1275 | npaths = o->npaths; | ||
1276 | else | ||
1277 | npaths = 1; | ||
1278 | |||
1279 | for (j = 0; j < npaths; j++) { | ||
1280 | printf("%s = ", buf); | ||
1281 | print(j, s, o); | ||
1282 | putchar('\n'); | ||
1283 | } | ||
1284 | } else { | ||
1285 | printf("%s\n", buf); | ||
1286 | } | ||
1287 | } | ||
1288 | |||
1289 | free_sets(s); | ||
1290 | |||
1291 | return 0; | ||
1292 | } | ||
1293 | |||
1294 | /* vim: set shiftwidth=4 tabstop=8: */ | ||
diff --git a/apps/plugins/puzzles/src/unfinished/path.c b/apps/plugins/puzzles/src/unfinished/path.c new file mode 100644 index 0000000000..2515ed0b71 --- /dev/null +++ b/apps/plugins/puzzles/src/unfinished/path.c | |||
@@ -0,0 +1,866 @@ | |||
1 | /* | ||
2 | * Experimental grid generator for Nikoli's `Number Link' puzzle. | ||
3 | */ | ||
4 | |||
5 | #include <stdio.h> | ||
6 | #include <stdlib.h> | ||
7 | #include <string.h> | ||
8 | #include <assert.h> | ||
9 | #include "puzzles.h" | ||
10 | |||
11 | /* | ||
12 | * 2005-07-08: This is currently a Path grid generator which will | ||
13 | * construct valid grids at a plausible speed. However, the grids | ||
14 | * are not of suitable quality to be used directly as puzzles. | ||
15 | * | ||
16 | * The basic strategy is to start with an empty grid, and | ||
17 | * repeatedly either (a) add a new path to it, or (b) extend one | ||
18 | * end of a path by one square in some direction and push other | ||
19 | * paths into new shapes in the process. The effect of this is that | ||
20 | * we are able to construct a set of paths which between them fill | ||
21 | * the entire grid. | ||
22 | * | ||
23 | * Quality issues: if we set the main loop to do (a) where possible | ||
24 | * and (b) only where necessary, we end up with a grid containing a | ||
25 | * few too many small paths, which therefore doesn't make for an | ||
26 | * interesting puzzle. If we reverse the priority so that we do (b) | ||
27 | * where possible and (a) only where necessary, we end up with some | ||
28 | * staggeringly interwoven grids with very very few separate paths, | ||
29 | * but the result of this is that there's invariably a solution | ||
30 | * other than the intended one which leaves many grid squares | ||
31 | * unfilled. There's also a separate problem which is that many | ||
32 | * grids have really boring and obvious paths in them, such as the | ||
33 | * entire bottom row of the grid being taken up by a single path. | ||
34 | * | ||
35 | * It's not impossible that a few tweaks might eliminate or reduce | ||
36 | * the incidence of boring paths, and might also find a happy | ||
37 | * medium between too many and too few. There remains the question | ||
38 | * of unique solutions, however. I fear there is no alternative but | ||
39 | * to write - somehow! - a solver. | ||
40 | * | ||
41 | * While I'm here, some notes on UI strategy for the parts of the | ||
42 | * puzzle implementation that _aren't_ the generator: | ||
43 | * | ||
44 | * - data model is to track connections between adjacent squares, | ||
45 | * so that you aren't limited to extending a path out from each | ||
46 | * number but can also mark sections of path which you know | ||
47 | * _will_ come in handy later. | ||
48 | * | ||
49 | * - user interface is to click in one square and drag to an | ||
50 | * adjacent one, thus creating a link between them. We can | ||
51 | * probably tolerate rapid mouse motion causing a drag directly | ||
52 | * to a square which is a rook move away, but any other rapid | ||
53 | * motion is ambiguous and probably the best option is to wait | ||
54 | * until the mouse returns to a square we know how to reach. | ||
55 | * | ||
56 | * - a drag causing the current path to backtrack has the effect | ||
57 | * of removing bits of it. | ||
58 | * | ||
59 | * - the UI should enforce at all times the constraint that at | ||
60 | * most two links can come into any square. | ||
61 | * | ||
62 | * - my Cunning Plan for actually implementing this: the game_ui | ||
63 | * contains a grid-sized array, which is copied from the current | ||
64 | * game_state on starting a drag. While a drag is active, the | ||
65 | * contents of the game_ui is adjusted with every mouse motion, | ||
66 | * and is displayed _in place_ of the game_state itself. On | ||
67 | * termination of a drag, the game_ui array is copied back into | ||
68 | * the new game_state (or rather, a string move is encoded which | ||
69 | * has precisely the set of link changes to cause that effect). | ||
70 | */ | ||
71 | |||
72 | /* | ||
73 | * 2020-05-11: some thoughts on a solver. | ||
74 | * | ||
75 | * Consider this example puzzle, from Wikipedia: | ||
76 | * | ||
77 | * ---4--- | ||
78 | * -3--25- | ||
79 | * ---31-- | ||
80 | * ---5--- | ||
81 | * ------- | ||
82 | * --1---- | ||
83 | * 2---4-- | ||
84 | * | ||
85 | * The kind of deduction that a human wants to make here is: which way | ||
86 | * does the path between the 4s go? In particular, does it go round | ||
87 | * the left of the W-shaped cluster of endpoints, or round the right | ||
88 | * of it? It's clear at a glance that it must go to the right, because | ||
89 | * _any_ path between the 4s that goes to the left of that cluster, no | ||
90 | * matter what detailed direction it takes, will disconnect the | ||
91 | * remaining grid squares into two components, with the two 2s not in | ||
92 | * the same component. So we immediately know that the path between | ||
93 | * the 4s _must_ go round the right-hand side of the grid. | ||
94 | * | ||
95 | * How do you model that global and topological reasoning in a | ||
96 | * computer? | ||
97 | * | ||
98 | * The most plausible idea I've seen so far is to use fundamental | ||
99 | * groups. The fundamental group of loops based at a given point in a | ||
100 | * space is a free group, under loop concatenation and up to homotopy, | ||
101 | * generated by the loops that go in each direction around each hole | ||
102 | * in the space. In this case, the 'holes' are clues, or connected | ||
103 | * groups of clues. | ||
104 | * | ||
105 | * So you might be able to enumerate all the homotopy classes of paths | ||
106 | * between (say) the two 4s as follows. Start with any old path | ||
107 | * between them (say, find the first one that breadth-first search | ||
108 | * will give you). Choose one of the 4s to regard as the base point | ||
109 | * (arbitrarily). Then breadth-first search among the space of _paths_ | ||
110 | * by the following procedure. Given a candidate path, append to it | ||
111 | * each of the possible loops that starts from the base point, | ||
112 | * circumnavigates one clue cluster, and returns to the base point. | ||
113 | * The result will typically be a path that retraces its steps and | ||
114 | * self-intersects. Now adjust it homotopically so that it doesn't. If | ||
115 | * that can't be done, then we haven't generated a fresh candidate | ||
116 | * path; if it can, then we've got a new path that is not homotopic to | ||
117 | * any path we already had, so add it to our list and queue it up to | ||
118 | * become the starting point of this search later. | ||
119 | * | ||
120 | * The idea is that this should exhaustively enumerate, up to | ||
121 | * homotopy, the different ways in which the two 4s can connect to | ||
122 | * each other within the constraint that you have to actually fit the | ||
123 | * path non-self-intersectingly into this grid. Then you can keep a | ||
124 | * list of those homotopy classes in mind, and start ruling them out | ||
125 | * by techniques like the connectivity approach described above. | ||
126 | * Hopefully you end up narrowing down to few enough homotopy classes | ||
127 | * that you can deduce something concrete about actual squares of the | ||
128 | * grid - for example, here, that if the path between 4s has to go | ||
129 | * round the right, then we know some specific squares it must go | ||
130 | * through, so we can fill those in. And then, having filled in a | ||
131 | * piece of the middle of a path, you can now regard connecting the | ||
132 | * ultimate endpoints to that mid-section as two separate subproblems, | ||
133 | * so you've reduced to a simpler instance of the same puzzle. | ||
134 | * | ||
135 | * But I don't know whether all of this actually works. I more or less | ||
136 | * believe the process for enumerating elements of the free group; but | ||
137 | * I'm not as confident that when you find a group element that won't | ||
138 | * fit in the grid, you'll never have to consider its descendants in | ||
139 | * the BFS either. And I'm assuming that 'unwind the self-intersection | ||
140 | * homotopically' is a thing that can actually be turned into a | ||
141 | * sensible algorithm. | ||
142 | * | ||
143 | * -------- | ||
144 | * | ||
145 | * Another thing that might be needed is to characterise _which_ | ||
146 | * homotopy class a given path is in. | ||
147 | * | ||
148 | * For this I think it's sufficient to choose a collection of paths | ||
149 | * along the _edges_ of the square grid, each of which connects two of | ||
150 | * the holes in the grid (including the grid exterior, which counts as | ||
151 | * a huge hole), such that they form a spanning tree between the | ||
152 | * holes. Then assign each of those paths an orientation, so that | ||
153 | * crossing it in one direction counts as 'positive' and the other | ||
154 | * 'negative'. Now analyse a candidate path from one square to another | ||
155 | * by following it and noting down which of those paths it crosses in | ||
156 | * which direction, then simplifying the result like a free group word | ||
157 | * (i.e. adjacent + and - crossings of the same path cancel out). | ||
158 | * | ||
159 | * -------- | ||
160 | * | ||
161 | * If we choose those paths to be of minimal length, then we can get | ||
162 | * an upper bound on the number of homotopy classes by observing that | ||
163 | * you can't traverse any of those barriers more times than will fit | ||
164 | * non-self-intersectingly in the grid. That might be an alternative | ||
165 | * method of bounding the search through the fundamental group to only | ||
166 | * finitely many possibilities. | ||
167 | */ | ||
168 | |||
169 | /* | ||
170 | * Standard notation for directions. | ||
171 | */ | ||
172 | #define L 0 | ||
173 | #define U 1 | ||
174 | #define R 2 | ||
175 | #define D 3 | ||
176 | #define DX(dir) ( (dir)==L ? -1 : (dir)==R ? +1 : 0) | ||
177 | #define DY(dir) ( (dir)==U ? -1 : (dir)==D ? +1 : 0) | ||
178 | |||
179 | /* | ||
180 | * Perform a breadth-first search over a grid of squares with the | ||
181 | * colour of square (X,Y) given by grid[Y*w+X]. The search begins | ||
182 | * at (x,y), and finds all squares which are the same colour as | ||
183 | * (x,y) and reachable from it by orthogonal moves. On return: | ||
184 | * - dist[Y*w+X] gives the distance of (X,Y) from (x,y), or -1 if | ||
185 | * unreachable or a different colour | ||
186 | * - the returned value is the number of reachable squares, | ||
187 | * including (x,y) itself | ||
188 | * - list[0] up to list[returned value - 1] list those squares, in | ||
189 | * increasing order of distance from (x,y) (and in arbitrary | ||
190 | * order within that). | ||
191 | */ | ||
192 | static int bfs(int w, int h, int *grid, int x, int y, int *dist, int *list) | ||
193 | { | ||
194 | int i, j, c, listsize, listdone; | ||
195 | |||
196 | /* | ||
197 | * Start by clearing the output arrays. | ||
198 | */ | ||
199 | for (i = 0; i < w*h; i++) | ||
200 | dist[i] = list[i] = -1; | ||
201 | |||
202 | /* | ||
203 | * Set up the initial list. | ||
204 | */ | ||
205 | listsize = 1; | ||
206 | listdone = 0; | ||
207 | list[0] = y*w+x; | ||
208 | dist[y*w+x] = 0; | ||
209 | c = grid[y*w+x]; | ||
210 | |||
211 | /* | ||
212 | * Repeatedly process a square and add any extra squares to the | ||
213 | * end of list. | ||
214 | */ | ||
215 | while (listdone < listsize) { | ||
216 | i = list[listdone++]; | ||
217 | y = i / w; | ||
218 | x = i % w; | ||
219 | for (j = 0; j < 4; j++) { | ||
220 | int xx, yy, ii; | ||
221 | |||
222 | xx = x + DX(j); | ||
223 | yy = y + DY(j); | ||
224 | ii = yy*w+xx; | ||
225 | |||
226 | if (xx >= 0 && xx < w && yy >= 0 && yy < h && | ||
227 | grid[ii] == c && dist[ii] == -1) { | ||
228 | dist[ii] = dist[i] + 1; | ||
229 | assert(listsize < w*h); | ||
230 | list[listsize++] = ii; | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | |||
235 | return listsize; | ||
236 | } | ||
237 | |||
238 | struct genctx { | ||
239 | int w, h; | ||
240 | int *grid, *sparegrid, *sparegrid2, *sparegrid3; | ||
241 | int *dist, *list; | ||
242 | |||
243 | int npaths, pathsize; | ||
244 | int *pathends, *sparepathends; /* 2*npaths entries */ | ||
245 | int *pathspare; /* npaths entries */ | ||
246 | int *extends; /* 8*npaths entries */ | ||
247 | }; | ||
248 | |||
249 | static struct genctx *new_genctx(int w, int h) | ||
250 | { | ||
251 | struct genctx *ctx = snew(struct genctx); | ||
252 | ctx->w = w; | ||
253 | ctx->h = h; | ||
254 | ctx->grid = snewn(w * h, int); | ||
255 | ctx->sparegrid = snewn(w * h, int); | ||
256 | ctx->sparegrid2 = snewn(w * h, int); | ||
257 | ctx->sparegrid3 = snewn(w * h, int); | ||
258 | ctx->dist = snewn(w * h, int); | ||
259 | ctx->list = snewn(w * h, int); | ||
260 | ctx->npaths = ctx->pathsize = 0; | ||
261 | ctx->pathends = ctx->sparepathends = ctx->pathspare = ctx->extends = NULL; | ||
262 | return ctx; | ||
263 | } | ||
264 | |||
265 | static void free_genctx(struct genctx *ctx) | ||
266 | { | ||
267 | sfree(ctx->grid); | ||
268 | sfree(ctx->sparegrid); | ||
269 | sfree(ctx->sparegrid2); | ||
270 | sfree(ctx->sparegrid3); | ||
271 | sfree(ctx->dist); | ||
272 | sfree(ctx->list); | ||
273 | sfree(ctx->pathends); | ||
274 | sfree(ctx->sparepathends); | ||
275 | sfree(ctx->pathspare); | ||
276 | sfree(ctx->extends); | ||
277 | } | ||
278 | |||
279 | static int newpath(struct genctx *ctx) | ||
280 | { | ||
281 | int n; | ||
282 | |||
283 | n = ctx->npaths++; | ||
284 | if (ctx->npaths > ctx->pathsize) { | ||
285 | ctx->pathsize += 16; | ||
286 | ctx->pathends = sresize(ctx->pathends, ctx->pathsize*2, int); | ||
287 | ctx->sparepathends = sresize(ctx->sparepathends, ctx->pathsize*2, int); | ||
288 | ctx->pathspare = sresize(ctx->pathspare, ctx->pathsize, int); | ||
289 | ctx->extends = sresize(ctx->extends, ctx->pathsize*8, int); | ||
290 | } | ||
291 | return n; | ||
292 | } | ||
293 | |||
294 | static int is_endpoint(struct genctx *ctx, int x, int y) | ||
295 | { | ||
296 | int w = ctx->w, h = ctx->h, c; | ||
297 | |||
298 | assert(x >= 0 && x < w && y >= 0 && y < h); | ||
299 | |||
300 | c = ctx->grid[y*w+x]; | ||
301 | if (c < 0) | ||
302 | return false; /* empty square is not an endpoint! */ | ||
303 | assert(c >= 0 && c < ctx->npaths); | ||
304 | if (ctx->pathends[c*2] == y*w+x || ctx->pathends[c*2+1] == y*w+x) | ||
305 | return true; | ||
306 | return false; | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * Tries to extend a path by one square in the given direction, | ||
311 | * pushing other paths around if necessary. Returns true on success | ||
312 | * or false on failure. | ||
313 | */ | ||
314 | static int extend_path(struct genctx *ctx, int path, int end, int direction) | ||
315 | { | ||
316 | int w = ctx->w, h = ctx->h; | ||
317 | int x, y, xe, ye, cut; | ||
318 | int i, j, jp, n, first, last; | ||
319 | |||
320 | assert(path >= 0 && path < ctx->npaths); | ||
321 | assert(end == 0 || end == 1); | ||
322 | |||
323 | /* | ||
324 | * Find the endpoint of the path and the point we plan to | ||
325 | * extend it into. | ||
326 | */ | ||
327 | y = ctx->pathends[path * 2 + end] / w; | ||
328 | x = ctx->pathends[path * 2 + end] % w; | ||
329 | assert(x >= 0 && x < w && y >= 0 && y < h); | ||
330 | |||
331 | xe = x + DX(direction); | ||
332 | ye = y + DY(direction); | ||
333 | if (xe < 0 || xe >= w || ye < 0 || ye >= h) | ||
334 | return false; /* could not extend in this direction */ | ||
335 | |||
336 | /* | ||
337 | * We don't extend paths _directly_ into endpoints of other | ||
338 | * paths, although we don't mind too much if a knock-on effect | ||
339 | * of an extension is to push part of another path into a third | ||
340 | * path's endpoint. | ||
341 | */ | ||
342 | if (is_endpoint(ctx, xe, ye)) | ||
343 | return false; | ||
344 | |||
345 | /* | ||
346 | * We can't extend a path back the way it came. | ||
347 | */ | ||
348 | if (ctx->grid[ye*w+xe] == path) | ||
349 | return false; | ||
350 | |||
351 | /* | ||
352 | * Paths may not double back on themselves. Check if the new | ||
353 | * point is adjacent to any point of this path other than (x,y). | ||
354 | */ | ||
355 | for (j = 0; j < 4; j++) { | ||
356 | int xf, yf; | ||
357 | |||
358 | xf = xe + DX(j); | ||
359 | yf = ye + DY(j); | ||
360 | |||
361 | if (xf >= 0 && xf < w && yf >= 0 && yf < h && | ||
362 | (xf != x || yf != y) && ctx->grid[yf*w+xf] == path) | ||
363 | return false; | ||
364 | } | ||
365 | |||
366 | /* | ||
367 | * Now we're convinced it's valid to _attempt_ the extension. | ||
368 | * It may still fail if we run out of space to push other paths | ||
369 | * into. | ||
370 | * | ||
371 | * So now we can set up our temporary data structures. We will | ||
372 | * need: | ||
373 | * | ||
374 | * - a spare copy of the grid on which to gradually move paths | ||
375 | * around (sparegrid) | ||
376 | * | ||
377 | * - a second spare copy with which to remember how paths | ||
378 | * looked just before being cut (sparegrid2). FIXME: is | ||
379 | * sparegrid2 necessary? right now it's never different from | ||
380 | * grid itself | ||
381 | * | ||
382 | * - a third spare copy with which to do the internal | ||
383 | * calculations involved in reconstituting a cut path | ||
384 | * (sparegrid3) | ||
385 | * | ||
386 | * - something to track which paths currently need | ||
387 | * reconstituting after being cut, and which have already | ||
388 | * been cut (pathspare) | ||
389 | * | ||
390 | * - a spare copy of pathends to store the altered states in | ||
391 | * (sparepathends) | ||
392 | */ | ||
393 | memcpy(ctx->sparegrid, ctx->grid, w*h*sizeof(int)); | ||
394 | memcpy(ctx->sparegrid2, ctx->grid, w*h*sizeof(int)); | ||
395 | memcpy(ctx->sparepathends, ctx->pathends, ctx->npaths*2*sizeof(int)); | ||
396 | for (i = 0; i < ctx->npaths; i++) | ||
397 | ctx->pathspare[i] = 0; /* 0=untouched, 1=broken, 2=fixed */ | ||
398 | |||
399 | /* | ||
400 | * Working in sparegrid, actually extend the path. If it cuts | ||
401 | * another, begin a loop in which we restore any cut path by | ||
402 | * moving it out of the way. | ||
403 | */ | ||
404 | cut = ctx->sparegrid[ye*w+xe]; | ||
405 | ctx->sparegrid[ye*w+xe] = path; | ||
406 | ctx->sparepathends[path*2+end] = ye*w+xe; | ||
407 | ctx->pathspare[path] = 2; /* this one is sacrosanct */ | ||
408 | if (cut >= 0) { | ||
409 | assert(cut >= 0 && cut < ctx->npaths); | ||
410 | ctx->pathspare[cut] = 1; /* broken */ | ||
411 | |||
412 | while (1) { | ||
413 | for (i = 0; i < ctx->npaths; i++) | ||
414 | if (ctx->pathspare[i] == 1) | ||
415 | break; | ||
416 | if (i == ctx->npaths) | ||
417 | break; /* we're done */ | ||
418 | |||
419 | /* | ||
420 | * Path i needs restoring. So walk along its original | ||
421 | * track (as given in sparegrid2) and see where it's | ||
422 | * been cut. Where it has, surround the cut points in | ||
423 | * the same colour, without overwriting already-fixed | ||
424 | * paths. | ||
425 | */ | ||
426 | memcpy(ctx->sparegrid3, ctx->sparegrid, w*h*sizeof(int)); | ||
427 | n = bfs(w, h, ctx->sparegrid2, | ||
428 | ctx->pathends[i*2] % w, ctx->pathends[i*2] / w, | ||
429 | ctx->dist, ctx->list); | ||
430 | first = last = -1; | ||
431 | if (ctx->sparegrid3[ctx->pathends[i*2]] != i || | ||
432 | ctx->sparegrid3[ctx->pathends[i*2+1]] != i) return false;/* FIXME */ | ||
433 | for (j = 0; j < n; j++) { | ||
434 | jp = ctx->list[j]; | ||
435 | assert(ctx->dist[jp] == j); | ||
436 | assert(ctx->sparegrid2[jp] == i); | ||
437 | |||
438 | /* | ||
439 | * Wipe out the original path in sparegrid. | ||
440 | */ | ||
441 | if (ctx->sparegrid[jp] == i) | ||
442 | ctx->sparegrid[jp] = -1; | ||
443 | |||
444 | /* | ||
445 | * Be prepared to shorten the path at either end if | ||
446 | * the endpoints have been stomped on. | ||
447 | */ | ||
448 | if (ctx->sparegrid3[jp] == i) { | ||
449 | if (first < 0) | ||
450 | first = jp; | ||
451 | last = jp; | ||
452 | } | ||
453 | |||
454 | if (ctx->sparegrid3[jp] != i) { | ||
455 | int jx = jp % w, jy = jp / w; | ||
456 | int dx, dy; | ||
457 | for (dy = -1; dy <= +1; dy++) | ||
458 | for (dx = -1; dx <= +1; dx++) { | ||
459 | int newp, newv; | ||
460 | if (!dy && !dx) | ||
461 | continue; /* central square */ | ||
462 | if (jx+dx < 0 || jx+dx >= w || | ||
463 | jy+dy < 0 || jy+dy >= h) | ||
464 | continue; /* out of range */ | ||
465 | newp = (jy+dy)*w+(jx+dx); | ||
466 | newv = ctx->sparegrid3[newp]; | ||
467 | if (newv >= 0 && (newv == i || | ||
468 | ctx->pathspare[newv] == 2)) | ||
469 | continue; /* can't use this square */ | ||
470 | ctx->sparegrid3[newp] = i; | ||
471 | } | ||
472 | } | ||
473 | } | ||
474 | |||
475 | if (first < 0 || last < 0) | ||
476 | return false; /* path is completely wiped out! */ | ||
477 | |||
478 | /* | ||
479 | * Now we've covered sparegrid3 in possible squares for | ||
480 | * the new layout of path i. Find the actual layout | ||
481 | * we're going to use by bfs: we want the shortest path | ||
482 | * from one endpoint to the other. | ||
483 | */ | ||
484 | n = bfs(w, h, ctx->sparegrid3, first % w, first / w, | ||
485 | ctx->dist, ctx->list); | ||
486 | if (ctx->dist[last] < 2) { | ||
487 | /* | ||
488 | * Either there is no way to get between the path's | ||
489 | * endpoints, or the remaining endpoints simply | ||
490 | * aren't far enough apart to make the path viable | ||
491 | * any more. This means the entire push operation | ||
492 | * has failed. | ||
493 | */ | ||
494 | return false; | ||
495 | } | ||
496 | |||
497 | /* | ||
498 | * Write the new path into sparegrid. Also save the new | ||
499 | * endpoint locations, in case they've changed. | ||
500 | */ | ||
501 | jp = last; | ||
502 | j = ctx->dist[jp]; | ||
503 | while (1) { | ||
504 | int d; | ||
505 | |||
506 | if (ctx->sparegrid[jp] >= 0) { | ||
507 | if (ctx->pathspare[ctx->sparegrid[jp]] == 2) | ||
508 | return false; /* somehow we've hit a fixed path */ | ||
509 | ctx->pathspare[ctx->sparegrid[jp]] = 1; /* broken */ | ||
510 | } | ||
511 | ctx->sparegrid[jp] = i; | ||
512 | |||
513 | if (j == 0) | ||
514 | break; | ||
515 | |||
516 | /* | ||
517 | * Now look at the neighbours of jp to find one | ||
518 | * which has dist[] one less. | ||
519 | */ | ||
520 | for (d = 0; d < 4; d++) { | ||
521 | int jx = (jp % w) + DX(d), jy = (jp / w) + DY(d); | ||
522 | if (jx >= 0 && jx < w && jy >= 0 && jy < w && | ||
523 | ctx->dist[jy*w+jx] == j-1) { | ||
524 | jp = jy*w+jx; | ||
525 | j--; | ||
526 | break; | ||
527 | } | ||
528 | } | ||
529 | assert(d < 4); | ||
530 | } | ||
531 | |||
532 | ctx->sparepathends[i*2] = first; | ||
533 | ctx->sparepathends[i*2+1] = last; | ||
534 | /* printf("new ends of path %d: %d,%d\n", i, first, last); */ | ||
535 | ctx->pathspare[i] = 2; /* fixed */ | ||
536 | } | ||
537 | } | ||
538 | |||
539 | /* | ||
540 | * If we got here, the extension was successful! | ||
541 | */ | ||
542 | memcpy(ctx->grid, ctx->sparegrid, w*h*sizeof(int)); | ||
543 | memcpy(ctx->pathends, ctx->sparepathends, ctx->npaths*2*sizeof(int)); | ||
544 | return true; | ||
545 | } | ||
546 | |||
547 | /* | ||
548 | * Tries to add a new path to the grid. | ||
549 | */ | ||
550 | static int add_path(struct genctx *ctx, random_state *rs) | ||
551 | { | ||
552 | int w = ctx->w, h = ctx->h; | ||
553 | int i, ii, n; | ||
554 | |||
555 | /* | ||
556 | * Our strategy is: | ||
557 | * - randomly choose an empty square in the grid | ||
558 | * - do a BFS from that point to find a long path starting | ||
559 | * from it | ||
560 | * - if we run out of viable empty squares, return failure. | ||
561 | */ | ||
562 | |||
563 | /* | ||
564 | * Use `sparegrid' to collect a list of empty squares. | ||
565 | */ | ||
566 | n = 0; | ||
567 | for (i = 0; i < w*h; i++) | ||
568 | if (ctx->grid[i] == -1) | ||
569 | ctx->sparegrid[n++] = i; | ||
570 | |||
571 | /* | ||
572 | * Shuffle the grid. | ||
573 | */ | ||
574 | for (i = n; i-- > 1 ;) { | ||
575 | int k = random_upto(rs, i+1); | ||
576 | if (k != i) { | ||
577 | int t = ctx->sparegrid[i]; | ||
578 | ctx->sparegrid[i] = ctx->sparegrid[k]; | ||
579 | ctx->sparegrid[k] = t; | ||
580 | } | ||
581 | } | ||
582 | |||
583 | /* | ||
584 | * Loop over it trying to add paths. This looks like a | ||
585 | * horrifying N^4 algorithm (that is, (w*h)^2), but I predict | ||
586 | * that in fact the worst case will very rarely arise because | ||
587 | * when there's lots of grid space an attempt will succeed very | ||
588 | * quickly. | ||
589 | */ | ||
590 | for (ii = 0; ii < n; ii++) { | ||
591 | int i = ctx->sparegrid[ii]; | ||
592 | int y = i / w, x = i % w, nsq; | ||
593 | int r, c, j; | ||
594 | |||
595 | /* | ||
596 | * BFS from here to find long paths. | ||
597 | */ | ||
598 | nsq = bfs(w, h, ctx->grid, x, y, ctx->dist, ctx->list); | ||
599 | |||
600 | /* | ||
601 | * If there aren't any long enough, give up immediately. | ||
602 | */ | ||
603 | assert(nsq > 0); /* must be the start square at least! */ | ||
604 | if (ctx->dist[ctx->list[nsq-1]] < 3) | ||
605 | continue; | ||
606 | |||
607 | /* | ||
608 | * Find the first viable endpoint in ctx->list (i.e. the | ||
609 | * first point with distance at least three). I could | ||
610 | * binary-search for this, but that would be O(log N) | ||
611 | * whereas in fact I can get a constant time bound by just | ||
612 | * searching up from the start - after all, there can be at | ||
613 | * most 13 points at _less_ than distance 3 from the | ||
614 | * starting one! | ||
615 | */ | ||
616 | for (j = 0; j < nsq; j++) | ||
617 | if (ctx->dist[ctx->list[j]] >= 3) | ||
618 | break; | ||
619 | assert(j < nsq); /* we tested above that there was one */ | ||
620 | |||
621 | /* | ||
622 | * Now we know that any element of `list' between j and nsq | ||
623 | * would be valid in principle. However, we want a few long | ||
624 | * paths rather than many small ones, so select only those | ||
625 | * elements which are either the maximum length or one | ||
626 | * below it. | ||
627 | */ | ||
628 | while (ctx->dist[ctx->list[j]] + 1 < ctx->dist[ctx->list[nsq-1]]) | ||
629 | j++; | ||
630 | r = j + random_upto(rs, nsq - j); | ||
631 | j = ctx->list[r]; | ||
632 | |||
633 | /* | ||
634 | * And that's our endpoint. Mark the new path on the grid. | ||
635 | */ | ||
636 | c = newpath(ctx); | ||
637 | ctx->pathends[c*2] = i; | ||
638 | ctx->pathends[c*2+1] = j; | ||
639 | ctx->grid[j] = c; | ||
640 | while (j != i) { | ||
641 | int d, np, index, pts[4]; | ||
642 | np = 0; | ||
643 | for (d = 0; d < 4; d++) { | ||
644 | int xn = (j % w) + DX(d), yn = (j / w) + DY(d); | ||
645 | if (xn >= 0 && xn < w && yn >= 0 && yn < w && | ||
646 | ctx->dist[yn*w+xn] == ctx->dist[j] - 1) | ||
647 | pts[np++] = yn*w+xn; | ||
648 | } | ||
649 | if (np > 1) | ||
650 | index = random_upto(rs, np); | ||
651 | else | ||
652 | index = 0; | ||
653 | j = pts[index]; | ||
654 | ctx->grid[j] = c; | ||
655 | } | ||
656 | |||
657 | return true; | ||
658 | } | ||
659 | |||
660 | return false; | ||
661 | } | ||
662 | |||
663 | /* | ||
664 | * The main grid generation loop. | ||
665 | */ | ||
666 | static void gridgen_mainloop(struct genctx *ctx, random_state *rs) | ||
667 | { | ||
668 | int w = ctx->w, h = ctx->h; | ||
669 | int i, n; | ||
670 | |||
671 | /* | ||
672 | * The generation algorithm doesn't always converge. Loop round | ||
673 | * until it does. | ||
674 | */ | ||
675 | while (1) { | ||
676 | for (i = 0; i < w*h; i++) | ||
677 | ctx->grid[i] = -1; | ||
678 | ctx->npaths = 0; | ||
679 | |||
680 | while (1) { | ||
681 | /* | ||
682 | * See if the grid is full. | ||
683 | */ | ||
684 | for (i = 0; i < w*h; i++) | ||
685 | if (ctx->grid[i] < 0) | ||
686 | break; | ||
687 | if (i == w*h) | ||
688 | return; | ||
689 | |||
690 | #ifdef GENERATION_DIAGNOSTICS | ||
691 | { | ||
692 | int x, y; | ||
693 | for (y = 0; y < h; y++) { | ||
694 | printf("|"); | ||
695 | for (x = 0; x < w; x++) { | ||
696 | if (ctx->grid[y*w+x] >= 0) | ||
697 | printf("%2d", ctx->grid[y*w+x]); | ||
698 | else | ||
699 | printf(" ."); | ||
700 | } | ||
701 | printf(" |\n"); | ||
702 | } | ||
703 | } | ||
704 | #endif | ||
705 | /* | ||
706 | * Try adding a path. | ||
707 | */ | ||
708 | if (add_path(ctx, rs)) { | ||
709 | #ifdef GENERATION_DIAGNOSTICS | ||
710 | printf("added path\n"); | ||
711 | #endif | ||
712 | continue; | ||
713 | } | ||
714 | |||
715 | /* | ||
716 | * Try extending a path. First list all the possible | ||
717 | * extensions. | ||
718 | */ | ||
719 | for (i = 0; i < ctx->npaths * 8; i++) | ||
720 | ctx->extends[i] = i; | ||
721 | n = i; | ||
722 | |||
723 | /* | ||
724 | * Then shuffle the list. | ||
725 | */ | ||
726 | for (i = n; i-- > 1 ;) { | ||
727 | int k = random_upto(rs, i+1); | ||
728 | if (k != i) { | ||
729 | int t = ctx->extends[i]; | ||
730 | ctx->extends[i] = ctx->extends[k]; | ||
731 | ctx->extends[k] = t; | ||
732 | } | ||
733 | } | ||
734 | |||
735 | /* | ||
736 | * Now try each one in turn until one works. | ||
737 | */ | ||
738 | for (i = 0; i < n; i++) { | ||
739 | int p, d, e; | ||
740 | p = ctx->extends[i]; | ||
741 | d = p % 4; | ||
742 | p /= 4; | ||
743 | e = p % 2; | ||
744 | p /= 2; | ||
745 | |||
746 | #ifdef GENERATION_DIAGNOSTICS | ||
747 | printf("trying to extend path %d end %d (%d,%d) in dir %d\n", p, e, | ||
748 | ctx->pathends[p*2+e] % w, | ||
749 | ctx->pathends[p*2+e] / w, d); | ||
750 | #endif | ||
751 | if (extend_path(ctx, p, e, d)) { | ||
752 | #ifdef GENERATION_DIAGNOSTICS | ||
753 | printf("extended path %d end %d (%d,%d) in dir %d\n", p, e, | ||
754 | ctx->pathends[p*2+e] % w, | ||
755 | ctx->pathends[p*2+e] / w, d); | ||
756 | #endif | ||
757 | break; | ||
758 | } | ||
759 | } | ||
760 | |||
761 | if (i < n) | ||
762 | continue; | ||
763 | |||
764 | break; | ||
765 | } | ||
766 | } | ||
767 | } | ||
768 | |||
769 | /* | ||
770 | * Wrapper function which deals with the boring bits such as | ||
771 | * removing the solution from the generated grid, shuffling the | ||
772 | * numeric labels and creating/disposing of the context structure. | ||
773 | */ | ||
774 | static int *gridgen(int w, int h, random_state *rs) | ||
775 | { | ||
776 | struct genctx *ctx; | ||
777 | int *ret; | ||
778 | int i; | ||
779 | |||
780 | ctx = new_genctx(w, h); | ||
781 | |||
782 | gridgen_mainloop(ctx, rs); | ||
783 | |||
784 | /* | ||
785 | * There is likely to be an ordering bias in the numbers | ||
786 | * (longer paths on lower numbers due to there having been more | ||
787 | * grid space when laying them down). So we must shuffle the | ||
788 | * numbers. We use ctx->pathspare for this. | ||
789 | * | ||
790 | * This is also as good a time as any to shift to numbering | ||
791 | * from 1, for display to the user. | ||
792 | */ | ||
793 | for (i = 0; i < ctx->npaths; i++) | ||
794 | ctx->pathspare[i] = i+1; | ||
795 | for (i = ctx->npaths; i-- > 1 ;) { | ||
796 | int k = random_upto(rs, i+1); | ||
797 | if (k != i) { | ||
798 | int t = ctx->pathspare[i]; | ||
799 | ctx->pathspare[i] = ctx->pathspare[k]; | ||
800 | ctx->pathspare[k] = t; | ||
801 | } | ||
802 | } | ||
803 | |||
804 | /* FIXME: remove this at some point! */ | ||
805 | { | ||
806 | int y, x; | ||
807 | for (y = 0; y < h; y++) { | ||
808 | printf("|"); | ||
809 | for (x = 0; x < w; x++) { | ||
810 | assert(ctx->grid[y*w+x] >= 0); | ||
811 | printf("%2d", ctx->pathspare[ctx->grid[y*w+x]]); | ||
812 | } | ||
813 | printf(" |\n"); | ||
814 | } | ||
815 | printf("\n"); | ||
816 | } | ||
817 | |||
818 | /* | ||
819 | * Clear the grid, and write in just the endpoints. | ||
820 | */ | ||
821 | for (i = 0; i < w*h; i++) | ||
822 | ctx->grid[i] = 0; | ||
823 | for (i = 0; i < ctx->npaths; i++) { | ||
824 | ctx->grid[ctx->pathends[i*2]] = | ||
825 | ctx->grid[ctx->pathends[i*2+1]] = ctx->pathspare[i]; | ||
826 | } | ||
827 | |||
828 | ret = ctx->grid; | ||
829 | ctx->grid = NULL; | ||
830 | |||
831 | free_genctx(ctx); | ||
832 | |||
833 | return ret; | ||
834 | } | ||
835 | |||
836 | #ifdef TEST_GEN | ||
837 | |||
838 | #define TEST_GENERAL | ||
839 | |||
840 | int main(void) | ||
841 | { | ||
842 | int w = 10, h = 8; | ||
843 | random_state *rs = random_new("12345", 5); | ||
844 | int x, y, i, *grid; | ||
845 | |||
846 | for (i = 0; i < 10; i++) { | ||
847 | grid = gridgen(w, h, rs); | ||
848 | |||
849 | for (y = 0; y < h; y++) { | ||
850 | printf("|"); | ||
851 | for (x = 0; x < w; x++) { | ||
852 | if (grid[y*w+x] > 0) | ||
853 | printf("%2d", grid[y*w+x]); | ||
854 | else | ||
855 | printf(" ."); | ||
856 | } | ||
857 | printf(" |\n"); | ||
858 | } | ||
859 | printf("\n"); | ||
860 | |||
861 | sfree(grid); | ||
862 | } | ||
863 | |||
864 | return 0; | ||
865 | } | ||
866 | #endif | ||
diff --git a/apps/plugins/puzzles/src/unfinished/separate.c b/apps/plugins/puzzles/src/unfinished/separate.c new file mode 100644 index 0000000000..6ca07252ad --- /dev/null +++ b/apps/plugins/puzzles/src/unfinished/separate.c | |||
@@ -0,0 +1,861 @@ | |||
1 | /* | ||
2 | * separate.c: Implementation of `Block Puzzle', a Japanese-only | ||
3 | * Nikoli puzzle seen at | ||
4 | * http://www.nikoli.co.jp/ja/puzzles/block_puzzle/ | ||
5 | * | ||
6 | * It's difficult to be absolutely sure of the rules since online | ||
7 | * Japanese translators are so bad, but looking at the sample | ||
8 | * puzzle it seems fairly clear that the rules of this one are | ||
9 | * very simple. You have an mxn grid in which every square | ||
10 | * contains a letter, there are k distinct letters with k dividing | ||
11 | * mn, and every letter occurs the same number of times; your aim | ||
12 | * is to find a partition of the grid into disjoint k-ominoes such | ||
13 | * that each k-omino contains exactly one of each letter. | ||
14 | * | ||
15 | * (It may be that Nikoli always have m,n,k equal to one another. | ||
16 | * However, I don't see that that's critical to the puzzle; k|mn | ||
17 | * is the only really important constraint, and even that could | ||
18 | * probably be dispensed with if some squares were marked as | ||
19 | * unused.) | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | * Current status: only the solver/generator is yet written, and | ||
24 | * although working in principle it's _very_ slow. It generates | ||
25 | * 5x5n5 or 6x6n4 readily enough, 6x6n6 with a bit of effort, and | ||
26 | * 7x7n7 only with a serious strain. I haven't dared try it higher | ||
27 | * than that yet. | ||
28 | * | ||
29 | * One idea to speed it up is to implement more of the solver. | ||
30 | * Ideas I've so far had include: | ||
31 | * | ||
32 | * - Generalise the deduction currently expressed as `an | ||
33 | * undersized chain with only one direction to extend must take | ||
34 | * it'. More generally, the deduction should say `if all the | ||
35 | * possible k-ominoes containing a given chain also contain | ||
36 | * square x, then mark square x as part of that k-omino'. | ||
37 | * + For example, consider this case: | ||
38 | * | ||
39 | * a ? b This represents the top left of a board; the letters | ||
40 | * ? ? ? a,b,c do not represent the letters used in the puzzle, | ||
41 | * c ? ? but indicate that those three squares are known to be | ||
42 | * of different ominoes. Now if k >= 4, we can immediately | ||
43 | * deduce that the square midway between b and c belongs to the | ||
44 | * same omino as a, because there is no way we can make a 4-or- | ||
45 | * more-omino containing a which does not also contain that square. | ||
46 | * (Most easily seen by imagining cutting that square out of the | ||
47 | * grid; then, clearly, the omino containing a has only two | ||
48 | * squares to expand into, and needs at least three.) | ||
49 | * | ||
50 | * The key difficulty with this mode of reasoning is | ||
51 | * identifying such squares. I can't immediately think of a | ||
52 | * simple algorithm for finding them on a wholesale basis. | ||
53 | * | ||
54 | * - Bfs out from a chain looking for the letters it lacks. For | ||
55 | * example, in this situation (top three rows of a 7x7n7 grid): | ||
56 | * | ||
57 | * +-----------+-+ | ||
58 | * |E-A-F-B-C D|D| | ||
59 | * +------- || | ||
60 | * |E-C-G-D G|G E| | ||
61 | * +-+--- | | ||
62 | * |E|E G A B F A| | ||
63 | * | ||
64 | * In this situation we can be sure that the top left chain | ||
65 | * E-A-F-B-C does extend rightwards to the D, because there is | ||
66 | * no other D within reach of that chain. Note also that the | ||
67 | * bfs can skip squares which are known to belong to other | ||
68 | * ominoes than this one. | ||
69 | * | ||
70 | * (This deduction, I fear, should only be used in an | ||
71 | * emergency, because it relies on _all_ squares within range | ||
72 | * of the bfs having particular values and so using it during | ||
73 | * incremental generation rather nails down a lot of the grid.) | ||
74 | * | ||
75 | * It's conceivable that another thing we could do would be to | ||
76 | * increase the flexibility in the grid generator: instead of | ||
77 | * nailing down the _value_ of any square depended on, merely nail | ||
78 | * down its equivalence to other squares. Unfortunately this turns | ||
79 | * the letter-selection phase of generation into a general graph | ||
80 | * colouring problem (we must draw a graph with equivalence | ||
81 | * classes of squares as the vertices, and an edge between any two | ||
82 | * vertices representing equivalence classes which contain squares | ||
83 | * that share an omino, and then k-colour the result) and hence | ||
84 | * requires recursion, which bodes ill for something we're doing | ||
85 | * that many times per generation. | ||
86 | * | ||
87 | * I suppose a simple thing I could try would be tuning the retry | ||
88 | * count, just in case it's set too high or too low for efficient | ||
89 | * generation. | ||
90 | */ | ||
91 | |||
92 | #include <stdio.h> | ||
93 | #include <stdlib.h> | ||
94 | #include <string.h> | ||
95 | #include <assert.h> | ||
96 | #include <ctype.h> | ||
97 | #ifdef NO_TGMATH_H | ||
98 | # include <math.h> | ||
99 | #else | ||
100 | # include <tgmath.h> | ||
101 | #endif | ||
102 | |||
103 | #include "puzzles.h" | ||
104 | |||
105 | enum { | ||
106 | COL_BACKGROUND, | ||
107 | NCOLOURS | ||
108 | }; | ||
109 | |||
110 | struct game_params { | ||
111 | int w, h, k; | ||
112 | }; | ||
113 | |||
114 | struct game_state { | ||
115 | int FIXME; | ||
116 | }; | ||
117 | |||
118 | static game_params *default_params(void) | ||
119 | { | ||
120 | game_params *ret = snew(game_params); | ||
121 | |||
122 | ret->w = ret->h = ret->k = 5; /* FIXME: a bit bigger? */ | ||
123 | |||
124 | return ret; | ||
125 | } | ||
126 | |||
127 | static bool game_fetch_preset(int i, char **name, game_params **params) | ||
128 | { | ||
129 | return false; | ||
130 | } | ||
131 | |||
132 | static void free_params(game_params *params) | ||
133 | { | ||
134 | sfree(params); | ||
135 | } | ||
136 | |||
137 | static game_params *dup_params(const game_params *params) | ||
138 | { | ||
139 | game_params *ret = snew(game_params); | ||
140 | *ret = *params; /* structure copy */ | ||
141 | return ret; | ||
142 | } | ||
143 | |||
144 | static void decode_params(game_params *params, char const *string) | ||
145 | { | ||
146 | params->w = params->h = params->k = atoi(string); | ||
147 | while (*string && isdigit((unsigned char)*string)) string++; | ||
148 | if (*string == 'x') { | ||
149 | string++; | ||
150 | params->h = atoi(string); | ||
151 | while (*string && isdigit((unsigned char)*string)) string++; | ||
152 | } | ||
153 | if (*string == 'n') { | ||
154 | string++; | ||
155 | params->k = atoi(string); | ||
156 | while (*string && isdigit((unsigned char)*string)) string++; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | static char *encode_params(const game_params *params, bool full) | ||
161 | { | ||
162 | char buf[256]; | ||
163 | sprintf(buf, "%dx%dn%d", params->w, params->h, params->k); | ||
164 | return dupstr(buf); | ||
165 | } | ||
166 | |||
167 | static config_item *game_configure(const game_params *params) | ||
168 | { | ||
169 | return NULL; | ||
170 | } | ||
171 | |||
172 | static game_params *custom_params(const config_item *cfg) | ||
173 | { | ||
174 | return NULL; | ||
175 | } | ||
176 | |||
177 | static const char *validate_params(const game_params *params, bool full) | ||
178 | { | ||
179 | return NULL; | ||
180 | } | ||
181 | |||
182 | /* ---------------------------------------------------------------------- | ||
183 | * Solver and generator. | ||
184 | */ | ||
185 | |||
186 | struct solver_scratch { | ||
187 | int w, h, k; | ||
188 | |||
189 | /* | ||
190 | * Tracks connectedness between squares. | ||
191 | */ | ||
192 | DSF *dsf; | ||
193 | |||
194 | /* | ||
195 | * size[dsf_canonify(dsf, yx)] tracks the size of the | ||
196 | * connected component containing yx. | ||
197 | */ | ||
198 | int *size; | ||
199 | |||
200 | /* | ||
201 | * contents[dsf_canonify(dsf, yx)*k+i] tracks whether or not | ||
202 | * the connected component containing yx includes letter i. If | ||
203 | * the value is -1, it doesn't; otherwise its value is the | ||
204 | * index in the main grid of the square which contributes that | ||
205 | * letter to the component. | ||
206 | */ | ||
207 | int *contents; | ||
208 | |||
209 | /* | ||
210 | * disconnect[dsf_canonify(dsf, yx1)*w*h + dsf_canonify(dsf, yx2)] | ||
211 | * tracks whether or not the connected components containing | ||
212 | * yx1 and yx2 are known to be distinct. | ||
213 | */ | ||
214 | bool *disconnect; | ||
215 | |||
216 | /* | ||
217 | * Temporary space used only inside particular solver loops. | ||
218 | */ | ||
219 | int *tmp; | ||
220 | }; | ||
221 | |||
222 | static struct solver_scratch *solver_scratch_new(int w, int h, int k) | ||
223 | { | ||
224 | int wh = w*h; | ||
225 | struct solver_scratch *sc = snew(struct solver_scratch); | ||
226 | |||
227 | sc->w = w; | ||
228 | sc->h = h; | ||
229 | sc->k = k; | ||
230 | |||
231 | sc->dsf = dsf_new(wh); | ||
232 | sc->size = snewn(wh, int); | ||
233 | sc->contents = snewn(wh * k, int); | ||
234 | sc->disconnect = snewn(wh*wh, bool); | ||
235 | sc->tmp = snewn(wh, int); | ||
236 | |||
237 | return sc; | ||
238 | } | ||
239 | |||
240 | static void solver_scratch_free(struct solver_scratch *sc) | ||
241 | { | ||
242 | dsf_free(sc->dsf); | ||
243 | sfree(sc->size); | ||
244 | sfree(sc->contents); | ||
245 | sfree(sc->disconnect); | ||
246 | sfree(sc->tmp); | ||
247 | sfree(sc); | ||
248 | } | ||
249 | |||
250 | static void solver_connect(struct solver_scratch *sc, int yx1, int yx2) | ||
251 | { | ||
252 | int w = sc->w, h = sc->h, k = sc->k; | ||
253 | int wh = w*h; | ||
254 | int i, yxnew; | ||
255 | |||
256 | yx1 = dsf_canonify(sc->dsf, yx1); | ||
257 | yx2 = dsf_canonify(sc->dsf, yx2); | ||
258 | assert(yx1 != yx2); | ||
259 | |||
260 | /* | ||
261 | * To connect two components together into a bigger one, we | ||
262 | * start by merging them in the dsf itself. | ||
263 | */ | ||
264 | dsf_merge(sc->dsf, yx1, yx2); | ||
265 | yxnew = dsf_canonify(sc->dsf, yx2); | ||
266 | |||
267 | /* | ||
268 | * The size of the new component is the sum of the sizes of the | ||
269 | * old ones. | ||
270 | */ | ||
271 | sc->size[yxnew] = sc->size[yx1] + sc->size[yx2]; | ||
272 | |||
273 | /* | ||
274 | * The contents bitmap of the new component is the union of the | ||
275 | * contents of the old ones. | ||
276 | * | ||
277 | * Given two numbers at most one of which is not -1, we can | ||
278 | * find the other one by adding the two and adding 1; this | ||
279 | * will yield -1 if both were -1 to begin with, otherwise the | ||
280 | * other. | ||
281 | * | ||
282 | * (A neater approach would be to take their bitwise AND, but | ||
283 | * this is unfortunately not well-defined standard C when done | ||
284 | * to signed integers.) | ||
285 | */ | ||
286 | for (i = 0; i < k; i++) { | ||
287 | assert(sc->contents[yx1*k+i] < 0 || sc->contents[yx2*k+i] < 0); | ||
288 | sc->contents[yxnew*k+i] = (sc->contents[yx1*k+i] + | ||
289 | sc->contents[yx2*k+i] + 1); | ||
290 | } | ||
291 | |||
292 | /* | ||
293 | * We must combine the rows _and_ the columns in the disconnect | ||
294 | * matrix. | ||
295 | */ | ||
296 | for (i = 0; i < wh; i++) | ||
297 | sc->disconnect[yxnew*wh+i] = (sc->disconnect[yx1*wh+i] || | ||
298 | sc->disconnect[yx2*wh+i]); | ||
299 | for (i = 0; i < wh; i++) | ||
300 | sc->disconnect[i*wh+yxnew] = (sc->disconnect[i*wh+yx1] || | ||
301 | sc->disconnect[i*wh+yx2]); | ||
302 | } | ||
303 | |||
304 | static void solver_disconnect(struct solver_scratch *sc, int yx1, int yx2) | ||
305 | { | ||
306 | int w = sc->w, h = sc->h; | ||
307 | int wh = w*h; | ||
308 | |||
309 | yx1 = dsf_canonify(sc->dsf, yx1); | ||
310 | yx2 = dsf_canonify(sc->dsf, yx2); | ||
311 | assert(yx1 != yx2); | ||
312 | assert(!sc->disconnect[yx1*wh+yx2]); | ||
313 | assert(!sc->disconnect[yx2*wh+yx1]); | ||
314 | |||
315 | /* | ||
316 | * Mark the components as disconnected from each other in the | ||
317 | * disconnect matrix. | ||
318 | */ | ||
319 | sc->disconnect[yx1*wh+yx2] = true; | ||
320 | sc->disconnect[yx2*wh+yx1] = true; | ||
321 | } | ||
322 | |||
323 | static void solver_init(struct solver_scratch *sc) | ||
324 | { | ||
325 | int w = sc->w, h = sc->h; | ||
326 | int wh = w*h; | ||
327 | int i; | ||
328 | |||
329 | /* | ||
330 | * Set up most of the scratch space. We don't set up the | ||
331 | * contents array, however, because this will change if we | ||
332 | * adjust the letter arrangement and re-run the solver. | ||
333 | */ | ||
334 | dsf_reinit(sc->dsf); | ||
335 | for (i = 0; i < wh; i++) sc->size[i] = 1; | ||
336 | memset(sc->disconnect, 0, wh*wh * sizeof(bool)); | ||
337 | } | ||
338 | |||
339 | static int solver_attempt(struct solver_scratch *sc, const unsigned char *grid, | ||
340 | bool *gen_lock) | ||
341 | { | ||
342 | int w = sc->w, h = sc->h, k = sc->k; | ||
343 | int wh = w*h; | ||
344 | int i, x, y; | ||
345 | bool done_something_overall = false; | ||
346 | |||
347 | /* | ||
348 | * Set up the contents array from the grid. | ||
349 | */ | ||
350 | for (i = 0; i < wh*k; i++) | ||
351 | sc->contents[i] = -1; | ||
352 | for (i = 0; i < wh; i++) | ||
353 | sc->contents[dsf_canonify(sc->dsf, i)*k+grid[i]] = i; | ||
354 | |||
355 | while (1) { | ||
356 | bool done_something = false; | ||
357 | |||
358 | /* | ||
359 | * Go over the grid looking for reasons to add to the | ||
360 | * disconnect matrix. We're after pairs of squares which: | ||
361 | * | ||
362 | * - are adjacent in the grid | ||
363 | * - belong to distinct dsf components | ||
364 | * - their components are not already marked as | ||
365 | * disconnected | ||
366 | * - their components share a letter in common. | ||
367 | */ | ||
368 | for (y = 0; y < h; y++) { | ||
369 | for (x = 0; x < w; x++) { | ||
370 | int dir; | ||
371 | for (dir = 0; dir < 2; dir++) { | ||
372 | int x2 = x + dir, y2 = y + 1 - dir; | ||
373 | int yx = y*w+x, yx2 = y2*w+x2; | ||
374 | |||
375 | if (x2 >= w || y2 >= h) | ||
376 | continue; /* one square is outside the grid */ | ||
377 | |||
378 | yx = dsf_canonify(sc->dsf, yx); | ||
379 | yx2 = dsf_canonify(sc->dsf, yx2); | ||
380 | if (yx == yx2) | ||
381 | continue; /* same dsf component */ | ||
382 | |||
383 | if (sc->disconnect[yx*wh+yx2]) | ||
384 | continue; /* already known disconnected */ | ||
385 | |||
386 | for (i = 0; i < k; i++) | ||
387 | if (sc->contents[yx*k+i] >= 0 && | ||
388 | sc->contents[yx2*k+i] >= 0) | ||
389 | break; | ||
390 | if (i == k) | ||
391 | continue; /* no letter in common */ | ||
392 | |||
393 | /* | ||
394 | * We've found one. Mark yx and yx2 as | ||
395 | * disconnected from each other. | ||
396 | */ | ||
397 | #ifdef SOLVER_DIAGNOSTICS | ||
398 | printf("Disconnecting %d and %d (%c)\n", yx, yx2, 'A'+i); | ||
399 | #endif | ||
400 | solver_disconnect(sc, yx, yx2); | ||
401 | done_something = done_something_overall = true; | ||
402 | |||
403 | /* | ||
404 | * We have just made a deduction which hinges | ||
405 | * on two particular grid squares being the | ||
406 | * same. If we are feeding back to a generator | ||
407 | * loop, we must therefore mark those squares | ||
408 | * as fixed in the generator, so that future | ||
409 | * rearrangement of the grid will not break | ||
410 | * the information on which we have already | ||
411 | * based deductions. | ||
412 | */ | ||
413 | if (gen_lock) { | ||
414 | gen_lock[sc->contents[yx*k+i]] = true; | ||
415 | gen_lock[sc->contents[yx2*k+i]] = true; | ||
416 | } | ||
417 | } | ||
418 | } | ||
419 | } | ||
420 | |||
421 | /* | ||
422 | * Now go over the grid looking for dsf components which | ||
423 | * are below maximum size and only have one way to extend, | ||
424 | * and extending them. | ||
425 | */ | ||
426 | for (i = 0; i < wh; i++) | ||
427 | sc->tmp[i] = -1; | ||
428 | for (y = 0; y < h; y++) { | ||
429 | for (x = 0; x < w; x++) { | ||
430 | int yx = dsf_canonify(sc->dsf, y*w+x); | ||
431 | int dir; | ||
432 | |||
433 | if (sc->size[yx] == k) | ||
434 | continue; | ||
435 | |||
436 | for (dir = 0; dir < 4; dir++) { | ||
437 | int x2 = x + (dir==0 ? -1 : dir==2 ? 1 : 0); | ||
438 | int y2 = y + (dir==1 ? -1 : dir==3 ? 1 : 0); | ||
439 | int yx2, yx2c; | ||
440 | |||
441 | if (y2 < 0 || y2 >= h || x2 < 0 || x2 >= w) | ||
442 | continue; | ||
443 | yx2 = y2*w+x2; | ||
444 | yx2c = dsf_canonify(sc->dsf, yx2); | ||
445 | |||
446 | if (yx2c != yx && !sc->disconnect[yx2c*wh+yx]) { | ||
447 | /* | ||
448 | * Component yx can be extended into square | ||
449 | * yx2. | ||
450 | */ | ||
451 | if (sc->tmp[yx] == -1) | ||
452 | sc->tmp[yx] = yx2; | ||
453 | else if (sc->tmp[yx] != yx2) | ||
454 | sc->tmp[yx] = -2; /* multiple choices found */ | ||
455 | } | ||
456 | } | ||
457 | } | ||
458 | } | ||
459 | for (i = 0; i < wh; i++) { | ||
460 | if (sc->tmp[i] >= 0) { | ||
461 | /* | ||
462 | * Make sure we haven't connected the two already | ||
463 | * during this loop (which could happen if for | ||
464 | * _both_ components this was the only way to | ||
465 | * extend them). | ||
466 | */ | ||
467 | if (dsf_canonify(sc->dsf, i) == | ||
468 | dsf_canonify(sc->dsf, sc->tmp[i])) | ||
469 | continue; | ||
470 | |||
471 | #ifdef SOLVER_DIAGNOSTICS | ||
472 | printf("Connecting %d and %d\n", i, sc->tmp[i]); | ||
473 | #endif | ||
474 | solver_connect(sc, i, sc->tmp[i]); | ||
475 | done_something = done_something_overall = true; | ||
476 | break; | ||
477 | } | ||
478 | } | ||
479 | |||
480 | if (!done_something) | ||
481 | break; | ||
482 | } | ||
483 | |||
484 | /* | ||
485 | * Return 0 if we haven't made any progress; 1 if we've done | ||
486 | * something but not solved it completely; 2 if we've solved | ||
487 | * it completely. | ||
488 | */ | ||
489 | for (i = 0; i < wh; i++) | ||
490 | if (sc->size[dsf_canonify(sc->dsf, i)] != k) | ||
491 | break; | ||
492 | if (i == wh) | ||
493 | return 2; | ||
494 | if (done_something_overall) | ||
495 | return 1; | ||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | static unsigned char *generate(int w, int h, int k, random_state *rs) | ||
500 | { | ||
501 | int wh = w*h; | ||
502 | int n = wh/k; | ||
503 | struct solver_scratch *sc; | ||
504 | unsigned char *grid; | ||
505 | unsigned char *shuffled; | ||
506 | int i, j, m, retries; | ||
507 | int *permutation; | ||
508 | bool *gen_lock; | ||
509 | |||
510 | sc = solver_scratch_new(w, h, k); | ||
511 | grid = snewn(wh, unsigned char); | ||
512 | shuffled = snewn(k, unsigned char); | ||
513 | permutation = snewn(wh, int); | ||
514 | gen_lock = snewn(wh, bool); | ||
515 | |||
516 | do { | ||
517 | DSF *dsf = divvy_rectangle(w, h, k, rs); | ||
518 | |||
519 | /* | ||
520 | * Go through the dsf and find the indices of all the | ||
521 | * squares involved in each omino, in a manner conducive | ||
522 | * to per-omino indexing. We set permutation[i*k+j] to be | ||
523 | * the index of the jth square (ordered arbitrarily) in | ||
524 | * omino i. | ||
525 | */ | ||
526 | for (i = j = 0; i < wh; i++) | ||
527 | if (dsf_canonify(dsf, i) == i) { | ||
528 | sc->tmp[i] = j; | ||
529 | /* | ||
530 | * During this loop and the following one, we use | ||
531 | * the last element of each row of permutation[] | ||
532 | * as a counter of the number of indices so far | ||
533 | * placed in it. When we place the final index of | ||
534 | * an omino, that counter is overwritten, but that | ||
535 | * doesn't matter because we'll never use it | ||
536 | * again. Of course this depends critically on | ||
537 | * divvy_rectangle() having returned correct | ||
538 | * results, or else chaos would ensue. | ||
539 | */ | ||
540 | permutation[j*k+k-1] = 0; | ||
541 | j++; | ||
542 | } | ||
543 | for (i = 0; i < wh; i++) { | ||
544 | j = sc->tmp[dsf_canonify(dsf, i)]; | ||
545 | m = permutation[j*k+k-1]++; | ||
546 | permutation[j*k+m] = i; | ||
547 | } | ||
548 | |||
549 | /* | ||
550 | * Track which squares' letters we have already depended | ||
551 | * on for deductions. This is gradually updated by | ||
552 | * solver_attempt(). | ||
553 | */ | ||
554 | memset(gen_lock, 0, wh * sizeof(bool)); | ||
555 | |||
556 | /* | ||
557 | * Now repeatedly fill the grid with letters, and attempt | ||
558 | * to solve it. If the solver makes progress but does not | ||
559 | * fail completely, then gen_lock will have been updated | ||
560 | * and we try again. On a complete failure, though, we | ||
561 | * have no option but to give up and abandon this set of | ||
562 | * ominoes. | ||
563 | */ | ||
564 | solver_init(sc); | ||
565 | retries = k*k; | ||
566 | while (1) { | ||
567 | /* | ||
568 | * Fill the grid with letters. We can safely use | ||
569 | * sc->tmp to hold the set of letters required at each | ||
570 | * stage, since it's at least size k and is currently | ||
571 | * unused. | ||
572 | */ | ||
573 | for (i = 0; i < n; i++) { | ||
574 | /* | ||
575 | * First, determine the set of letters already | ||
576 | * placed in this omino by gen_lock. | ||
577 | */ | ||
578 | for (j = 0; j < k; j++) | ||
579 | sc->tmp[j] = j; | ||
580 | for (j = 0; j < k; j++) { | ||
581 | int index = permutation[i*k+j]; | ||
582 | int letter = grid[index]; | ||
583 | if (gen_lock[index]) | ||
584 | sc->tmp[letter] = -1; | ||
585 | } | ||
586 | /* | ||
587 | * Now collect together all the remaining letters | ||
588 | * and randomly shuffle them. | ||
589 | */ | ||
590 | for (j = m = 0; j < k; j++) | ||
591 | if (sc->tmp[j] >= 0) | ||
592 | sc->tmp[m++] = sc->tmp[j]; | ||
593 | shuffle(sc->tmp, m, sizeof(*sc->tmp), rs); | ||
594 | /* | ||
595 | * Finally, write the shuffled letters into the | ||
596 | * grid. | ||
597 | */ | ||
598 | for (j = 0; j < k; j++) { | ||
599 | int index = permutation[i*k+j]; | ||
600 | if (!gen_lock[index]) | ||
601 | grid[index] = sc->tmp[--m]; | ||
602 | } | ||
603 | assert(m == 0); | ||
604 | } | ||
605 | |||
606 | /* | ||
607 | * Now we have a candidate grid. Attempt to progress | ||
608 | * the solution. | ||
609 | */ | ||
610 | m = solver_attempt(sc, grid, gen_lock); | ||
611 | if (m == 2 || /* success */ | ||
612 | (m == 0 && retries-- <= 0)) /* failure */ | ||
613 | break; | ||
614 | if (m == 1) | ||
615 | retries = k*k; /* reset this counter, and continue */ | ||
616 | } | ||
617 | |||
618 | dsf_free(dsf); | ||
619 | } while (m == 0); | ||
620 | |||
621 | sfree(gen_lock); | ||
622 | sfree(permutation); | ||
623 | sfree(shuffled); | ||
624 | solver_scratch_free(sc); | ||
625 | |||
626 | return grid; | ||
627 | } | ||
628 | |||
629 | /* ---------------------------------------------------------------------- | ||
630 | * End of solver/generator code. | ||
631 | */ | ||
632 | |||
633 | static char *new_game_desc(const game_params *params, random_state *rs, | ||
634 | char **aux, bool interactive) | ||
635 | { | ||
636 | int w = params->w, h = params->h, wh = w*h, k = params->k; | ||
637 | unsigned char *grid; | ||
638 | char *desc; | ||
639 | int i; | ||
640 | |||
641 | grid = generate(w, h, k, rs); | ||
642 | |||
643 | desc = snewn(wh+1, char); | ||
644 | for (i = 0; i < wh; i++) | ||
645 | desc[i] = 'A' + grid[i]; | ||
646 | desc[wh] = '\0'; | ||
647 | |||
648 | sfree(grid); | ||
649 | |||
650 | return desc; | ||
651 | } | ||
652 | |||
653 | static const char *validate_desc(const game_params *params, const char *desc) | ||
654 | { | ||
655 | return NULL; | ||
656 | } | ||
657 | |||
658 | static game_state *new_game(midend *me, const game_params *params, | ||
659 | const char *desc) | ||
660 | { | ||
661 | game_state *state = snew(game_state); | ||
662 | |||
663 | state->FIXME = 0; | ||
664 | |||
665 | return state; | ||
666 | } | ||
667 | |||
668 | static game_state *dup_game(const game_state *state) | ||
669 | { | ||
670 | game_state *ret = snew(game_state); | ||
671 | |||
672 | ret->FIXME = state->FIXME; | ||
673 | |||
674 | return ret; | ||
675 | } | ||
676 | |||
677 | static void free_game(game_state *state) | ||
678 | { | ||
679 | sfree(state); | ||
680 | } | ||
681 | |||
682 | static char *solve_game(const game_state *state, const game_state *currstate, | ||
683 | const char *aux, const char **error) | ||
684 | { | ||
685 | return NULL; | ||
686 | } | ||
687 | |||
688 | static bool game_can_format_as_text_now(const game_params *params) | ||
689 | { | ||
690 | return true; | ||
691 | } | ||
692 | |||
693 | static char *game_text_format(const game_state *state) | ||
694 | { | ||
695 | return NULL; | ||
696 | } | ||
697 | |||
698 | static game_ui *new_ui(const game_state *state) | ||
699 | { | ||
700 | return NULL; | ||
701 | } | ||
702 | |||
703 | static void free_ui(game_ui *ui) | ||
704 | { | ||
705 | } | ||
706 | |||
707 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | ||
708 | const game_state *newstate) | ||
709 | { | ||
710 | } | ||
711 | |||
712 | struct game_drawstate { | ||
713 | int tilesize; | ||
714 | int FIXME; | ||
715 | }; | ||
716 | |||
717 | static char *interpret_move(const game_state *state, game_ui *ui, | ||
718 | const game_drawstate *ds, | ||
719 | int x, int y, int button) | ||
720 | { | ||
721 | return NULL; | ||
722 | } | ||
723 | |||
724 | static game_state *execute_move(const game_state *state, const char *move) | ||
725 | { | ||
726 | return NULL; | ||
727 | } | ||
728 | |||
729 | /* ---------------------------------------------------------------------- | ||
730 | * Drawing routines. | ||
731 | */ | ||
732 | |||
733 | static void game_compute_size(const game_params *params, int tilesize, | ||
734 | const game_ui *ui, int *x, int *y) | ||
735 | { | ||
736 | *x = *y = 10 * tilesize; /* FIXME */ | ||
737 | } | ||
738 | |||
739 | static void game_set_size(drawing *dr, game_drawstate *ds, | ||
740 | const game_params *params, int tilesize) | ||
741 | { | ||
742 | ds->tilesize = tilesize; | ||
743 | } | ||
744 | |||
745 | static float *game_colours(frontend *fe, int *ncolours) | ||
746 | { | ||
747 | float *ret = snewn(3 * NCOLOURS, float); | ||
748 | |||
749 | frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); | ||
750 | |||
751 | *ncolours = NCOLOURS; | ||
752 | return ret; | ||
753 | } | ||
754 | |||
755 | static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) | ||
756 | { | ||
757 | struct game_drawstate *ds = snew(struct game_drawstate); | ||
758 | |||
759 | ds->tilesize = 0; | ||
760 | ds->FIXME = 0; | ||
761 | |||
762 | return ds; | ||
763 | } | ||
764 | |||
765 | static void game_free_drawstate(drawing *dr, game_drawstate *ds) | ||
766 | { | ||
767 | sfree(ds); | ||
768 | } | ||
769 | |||
770 | static void game_redraw(drawing *dr, game_drawstate *ds, | ||
771 | const game_state *oldstate, const game_state *state, | ||
772 | int dir, const game_ui *ui, | ||
773 | float animtime, float flashtime) | ||
774 | { | ||
775 | } | ||
776 | |||
777 | static float game_anim_length(const game_state *oldstate, | ||
778 | const game_state *newstate, int dir, game_ui *ui) | ||
779 | { | ||
780 | return 0.0F; | ||
781 | } | ||
782 | |||
783 | static float game_flash_length(const game_state *oldstate, | ||
784 | const game_state *newstate, int dir, game_ui *ui) | ||
785 | { | ||
786 | return 0.0F; | ||
787 | } | ||
788 | |||
789 | static void game_get_cursor_location(const game_ui *ui, | ||
790 | const game_drawstate *ds, | ||
791 | const game_state *state, | ||
792 | const game_params *params, | ||
793 | int *x, int *y, int *w, int *h) | ||
794 | { | ||
795 | } | ||
796 | |||
797 | static int game_status(const game_state *state) | ||
798 | { | ||
799 | return 0; | ||
800 | } | ||
801 | |||
802 | static bool game_timing_state(const game_state *state, game_ui *ui) | ||
803 | { | ||
804 | return true; | ||
805 | } | ||
806 | |||
807 | static void game_print_size(const game_params *params, const game_ui *ui, | ||
808 | float *x, float *y) | ||
809 | { | ||
810 | } | ||
811 | |||
812 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, | ||
813 | int tilesize) | ||
814 | { | ||
815 | } | ||
816 | |||
817 | #ifdef COMBINED | ||
818 | #define thegame separate | ||
819 | #endif | ||
820 | |||
821 | const struct game thegame = { | ||
822 | "Separate", NULL, NULL, | ||
823 | default_params, | ||
824 | game_fetch_preset, NULL, | ||
825 | decode_params, | ||
826 | encode_params, | ||
827 | free_params, | ||
828 | dup_params, | ||
829 | false, game_configure, custom_params, | ||
830 | validate_params, | ||
831 | new_game_desc, | ||
832 | validate_desc, | ||
833 | new_game, | ||
834 | dup_game, | ||
835 | free_game, | ||
836 | false, solve_game, | ||
837 | false, game_can_format_as_text_now, game_text_format, | ||
838 | NULL, NULL, /* get_prefs, set_prefs */ | ||
839 | new_ui, | ||
840 | free_ui, | ||
841 | NULL, /* encode_ui */ | ||
842 | NULL, /* decode_ui */ | ||
843 | NULL, /* game_request_keys */ | ||
844 | game_changed_state, | ||
845 | NULL, /* current_key_label */ | ||
846 | interpret_move, | ||
847 | execute_move, | ||
848 | 20 /* FIXME */, game_compute_size, game_set_size, | ||
849 | game_colours, | ||
850 | game_new_drawstate, | ||
851 | game_free_drawstate, | ||
852 | game_redraw, | ||
853 | game_anim_length, | ||
854 | game_flash_length, | ||
855 | game_get_cursor_location, | ||
856 | game_status, | ||
857 | false, false, game_print_size, game_print, | ||
858 | false, /* wants_statusbar */ | ||
859 | false, game_timing_state, | ||
860 | 0, /* flags */ | ||
861 | }; | ||
diff --git a/apps/plugins/puzzles/src/unfinished/slide.c b/apps/plugins/puzzles/src/unfinished/slide.c new file mode 100644 index 0000000000..4c4d98943e --- /dev/null +++ b/apps/plugins/puzzles/src/unfinished/slide.c | |||
@@ -0,0 +1,2444 @@ | |||
1 | /* | ||
2 | * slide.c: Implementation of the block-sliding puzzle `Klotski'. | ||
3 | */ | ||
4 | |||
5 | /* | ||
6 | * TODO: | ||
7 | * | ||
8 | * - Improve the generator. | ||
9 | * * actually, we seem to be mostly sensible already now. I | ||
10 | * want more choice over the type of main block and location | ||
11 | * of the exit/target, and I think I probably ought to give | ||
12 | * up on compactness and just bite the bullet and have the | ||
13 | * target area right outside the main wall, but mostly I | ||
14 | * think it's OK. | ||
15 | * * the move limit tends to make the game _slower_ to | ||
16 | * generate, which is odd. Perhaps investigate why. | ||
17 | * | ||
18 | * - Improve the graphics. | ||
19 | * * All the colours are a bit wishy-washy. _Some_ dark | ||
20 | * colours would surely not be excessive? Probably darken | ||
21 | * the tiles, the walls and the main block, and leave the | ||
22 | * target marker pale. | ||
23 | * * The cattle grid effect is still disgusting. Think of | ||
24 | * something completely different. | ||
25 | * * The highlight for next-piece-to-move in the solver is | ||
26 | * excessive, and the shadow blends in too well with the | ||
27 | * piece lowlights. Adjust both. | ||
28 | */ | ||
29 | |||
30 | #include <stdio.h> | ||
31 | #include <stdlib.h> | ||
32 | #include <string.h> | ||
33 | #include <assert.h> | ||
34 | #include <ctype.h> | ||
35 | #ifdef NO_TGMATH_H | ||
36 | # include <math.h> | ||
37 | #else | ||
38 | # include <tgmath.h> | ||
39 | #endif | ||
40 | |||
41 | #include "puzzles.h" | ||
42 | #include "tree234.h" | ||
43 | |||
44 | /* | ||
45 | * The implementation of this game revolves around the insight | ||
46 | * which makes an exhaustive-search solver feasible: although | ||
47 | * there are many blocks which can be rearranged in many ways, any | ||
48 | * two blocks of the same shape are _indistinguishable_ and hence | ||
49 | * the number of _distinct_ board layouts is generally much | ||
50 | * smaller. So we adopt a representation for board layouts which | ||
51 | * is inherently canonical, i.e. there are no two distinct | ||
52 | * representations which encode indistinguishable layouts. | ||
53 | * | ||
54 | * The way we do this is to encode each square of the board, in | ||
55 | * the normal left-to-right top-to-bottom order, as being one of | ||
56 | * the following things: | ||
57 | * - the first square (in the given order) of a block (`anchor') | ||
58 | * - special case of the above: the anchor for the _main_ block | ||
59 | * (i.e. the one which the aim of the game is to get to the | ||
60 | * target position) | ||
61 | * - a subsequent square of a block whose previous square was N | ||
62 | * squares ago | ||
63 | * - an impassable wall | ||
64 | * | ||
65 | * (We also separately store data about which board positions are | ||
66 | * forcefields only passable by the main block. We can't encode | ||
67 | * that in the main board data, because then the main block would | ||
68 | * destroy forcefields as it went over them.) | ||
69 | * | ||
70 | * Hence, for example, a 2x2 square block would be encoded as | ||
71 | * ANCHOR, followed by DIST(1), and w-2 squares later on there | ||
72 | * would be DIST(w-1) followed by DIST(1). So if you start at the | ||
73 | * last of those squares, the DIST numbers give you a linked list | ||
74 | * pointing back through all the other squares in the same block. | ||
75 | * | ||
76 | * So the solver simply does a bfs over all reachable positions, | ||
77 | * encoding them in this format and storing them in a tree234 to | ||
78 | * ensure it doesn't ever revisit an already-analysed position. | ||
79 | */ | ||
80 | |||
81 | enum { | ||
82 | /* | ||
83 | * The colours are arranged here so that every base colour is | ||
84 | * directly followed by its highlight colour and then its | ||
85 | * lowlight colour. Do not break this, or draw_tile() will get | ||
86 | * confused. | ||
87 | */ | ||
88 | COL_BACKGROUND, | ||
89 | COL_HIGHLIGHT, | ||
90 | COL_LOWLIGHT, | ||
91 | COL_DRAGGING, | ||
92 | COL_DRAGGING_HIGHLIGHT, | ||
93 | COL_DRAGGING_LOWLIGHT, | ||
94 | COL_MAIN, | ||
95 | COL_MAIN_HIGHLIGHT, | ||
96 | COL_MAIN_LOWLIGHT, | ||
97 | COL_MAIN_DRAGGING, | ||
98 | COL_MAIN_DRAGGING_HIGHLIGHT, | ||
99 | COL_MAIN_DRAGGING_LOWLIGHT, | ||
100 | COL_TARGET, | ||
101 | COL_TARGET_HIGHLIGHT, | ||
102 | COL_TARGET_LOWLIGHT, | ||
103 | NCOLOURS | ||
104 | }; | ||
105 | |||
106 | /* | ||
107 | * Board layout is a simple array of bytes. Each byte holds: | ||
108 | */ | ||
109 | #define ANCHOR 255 /* top-left-most square of some piece */ | ||
110 | #define MAINANCHOR 254 /* anchor of _main_ piece */ | ||
111 | #define EMPTY 253 /* empty square */ | ||
112 | #define WALL 252 /* immovable wall */ | ||
113 | #define MAXDIST 251 | ||
114 | /* all other values indicate distance back to previous square of same block */ | ||
115 | #define ISDIST(x) ( (unsigned char)((x)-1) <= MAXDIST-1 ) | ||
116 | #define DIST(x) (x) | ||
117 | #define ISANCHOR(x) ( (x)==ANCHOR || (x)==MAINANCHOR ) | ||
118 | #define ISBLOCK(x) ( ISANCHOR(x) || ISDIST(x) ) | ||
119 | |||
120 | /* | ||
121 | * MAXDIST is the largest DIST value we can encode. This must | ||
122 | * therefore also be the maximum puzzle width in theory (although | ||
123 | * solver running time will dictate a much smaller limit in | ||
124 | * practice). | ||
125 | */ | ||
126 | #define MAXWID MAXDIST | ||
127 | |||
128 | struct game_params { | ||
129 | int w, h; | ||
130 | int maxmoves; | ||
131 | }; | ||
132 | |||
133 | struct game_immutable_state { | ||
134 | int refcount; | ||
135 | bool *forcefield; | ||
136 | }; | ||
137 | |||
138 | struct game_solution { | ||
139 | int nmoves; | ||
140 | int *moves; /* just like from solve_board() */ | ||
141 | int refcount; | ||
142 | }; | ||
143 | |||
144 | struct game_state { | ||
145 | int w, h; | ||
146 | unsigned char *board; | ||
147 | int tx, ty; /* target coords for MAINANCHOR */ | ||
148 | int minmoves; /* for display only */ | ||
149 | int lastmoved, lastmoved_pos; /* for move counting */ | ||
150 | int movecount; | ||
151 | int completed; | ||
152 | bool cheated; | ||
153 | struct game_immutable_state *imm; | ||
154 | struct game_solution *soln; | ||
155 | int soln_index; | ||
156 | }; | ||
157 | |||
158 | static game_params *default_params(void) | ||
159 | { | ||
160 | game_params *ret = snew(game_params); | ||
161 | |||
162 | ret->w = 7; | ||
163 | ret->h = 6; | ||
164 | ret->maxmoves = 40; | ||
165 | |||
166 | return ret; | ||
167 | } | ||
168 | |||
169 | static const struct game_params slide_presets[] = { | ||
170 | {7, 6, 25}, | ||
171 | {7, 6, -1}, | ||
172 | {8, 6, -1}, | ||
173 | }; | ||
174 | |||
175 | static bool game_fetch_preset(int i, char **name, game_params **params) | ||
176 | { | ||
177 | game_params *ret; | ||
178 | char str[80]; | ||
179 | |||
180 | if (i < 0 || i >= lenof(slide_presets)) | ||
181 | return false; | ||
182 | |||
183 | ret = snew(game_params); | ||
184 | *ret = slide_presets[i]; | ||
185 | |||
186 | sprintf(str, "%dx%d", ret->w, ret->h); | ||
187 | if (ret->maxmoves >= 0) | ||
188 | sprintf(str + strlen(str), ", max %d moves", ret->maxmoves); | ||
189 | else | ||
190 | sprintf(str + strlen(str), ", no move limit"); | ||
191 | |||
192 | *name = dupstr(str); | ||
193 | *params = ret; | ||
194 | return true; | ||
195 | } | ||
196 | |||
197 | static void free_params(game_params *params) | ||
198 | { | ||
199 | sfree(params); | ||
200 | } | ||
201 | |||
202 | static game_params *dup_params(const game_params *params) | ||
203 | { | ||
204 | game_params *ret = snew(game_params); | ||
205 | *ret = *params; /* structure copy */ | ||
206 | return ret; | ||
207 | } | ||
208 | |||
209 | static void decode_params(game_params *params, char const *string) | ||
210 | { | ||
211 | params->w = params->h = atoi(string); | ||
212 | while (*string && isdigit((unsigned char)*string)) string++; | ||
213 | if (*string == 'x') { | ||
214 | string++; | ||
215 | params->h = atoi(string); | ||
216 | while (*string && isdigit((unsigned char)*string)) string++; | ||
217 | } | ||
218 | if (*string == 'm') { | ||
219 | string++; | ||
220 | params->maxmoves = atoi(string); | ||
221 | while (*string && isdigit((unsigned char)*string)) string++; | ||
222 | } else if (*string == 'u') { | ||
223 | string++; | ||
224 | params->maxmoves = -1; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | static char *encode_params(const game_params *params, bool full) | ||
229 | { | ||
230 | char data[256]; | ||
231 | |||
232 | sprintf(data, "%dx%d", params->w, params->h); | ||
233 | if (params->maxmoves >= 0) | ||
234 | sprintf(data + strlen(data), "m%d", params->maxmoves); | ||
235 | else | ||
236 | sprintf(data + strlen(data), "u"); | ||
237 | |||
238 | return dupstr(data); | ||
239 | } | ||
240 | |||
241 | static config_item *game_configure(const game_params *params) | ||
242 | { | ||
243 | config_item *ret; | ||
244 | char buf[80]; | ||
245 | |||
246 | ret = snewn(4, config_item); | ||
247 | |||
248 | ret[0].name = "Width"; | ||
249 | ret[0].type = C_STRING; | ||
250 | sprintf(buf, "%d", params->w); | ||
251 | ret[0].u.string.sval = dupstr(buf); | ||
252 | |||
253 | ret[1].name = "Height"; | ||
254 | ret[1].type = C_STRING; | ||
255 | sprintf(buf, "%d", params->h); | ||
256 | ret[1].u.string.sval = dupstr(buf); | ||
257 | |||
258 | ret[2].name = "Solution length limit"; | ||
259 | ret[2].type = C_STRING; | ||
260 | sprintf(buf, "%d", params->maxmoves); | ||
261 | ret[2].u.string.sval = dupstr(buf); | ||
262 | |||
263 | ret[3].name = NULL; | ||
264 | ret[3].type = C_END; | ||
265 | |||
266 | return ret; | ||
267 | } | ||
268 | |||
269 | static game_params *custom_params(const config_item *cfg) | ||
270 | { | ||
271 | game_params *ret = snew(game_params); | ||
272 | |||
273 | ret->w = atoi(cfg[0].u.string.sval); | ||
274 | ret->h = atoi(cfg[1].u.string.sval); | ||
275 | ret->maxmoves = atoi(cfg[2].u.string.sval); | ||
276 | |||
277 | return ret; | ||
278 | } | ||
279 | |||
280 | static const char *validate_params(const game_params *params, bool full) | ||
281 | { | ||
282 | if (params->w > MAXWID) | ||
283 | return "Width must be at most " STR(MAXWID); | ||
284 | |||
285 | if (params->w < 5) | ||
286 | return "Width must be at least 5"; | ||
287 | if (params->h < 4) | ||
288 | return "Height must be at least 4"; | ||
289 | |||
290 | return NULL; | ||
291 | } | ||
292 | |||
293 | static char *board_text_format(int w, int h, unsigned char *data, | ||
294 | bool *forcefield) | ||
295 | { | ||
296 | int wh = w*h; | ||
297 | DSF *dsf = dsf_new(wh); | ||
298 | int i, x, y; | ||
299 | int retpos, retlen = (w*2+2)*(h*2+1)+1; | ||
300 | char *ret = snewn(retlen, char); | ||
301 | |||
302 | for (i = 0; i < wh; i++) | ||
303 | if (ISDIST(data[i])) | ||
304 | dsf_merge(dsf, i - data[i], i); | ||
305 | retpos = 0; | ||
306 | for (y = 0; y < 2*h+1; y++) { | ||
307 | for (x = 0; x < 2*w+1; x++) { | ||
308 | int v; | ||
309 | int i = (y/2)*w+(x/2); | ||
310 | |||
311 | #define dtype(i) (ISBLOCK(data[i]) ? \ | ||
312 | dsf_canonify(dsf, i) : data[i]) | ||
313 | #define dchar(t) ((t)==EMPTY ? ' ' : (t)==WALL ? '#' : \ | ||
314 | data[t] == MAINANCHOR ? '*' : '%') | ||
315 | |||
316 | if (y % 2 && x % 2) { | ||
317 | int j = dtype(i); | ||
318 | v = dchar(j); | ||
319 | } else if (y % 2 && !(x % 2)) { | ||
320 | int j1 = (x > 0 ? dtype(i-1) : -1); | ||
321 | int j2 = (x < 2*w ? dtype(i) : -1); | ||
322 | if (j1 != j2) | ||
323 | v = '|'; | ||
324 | else | ||
325 | v = dchar(j1); | ||
326 | } else if (!(y % 2) && (x % 2)) { | ||
327 | int j1 = (y > 0 ? dtype(i-w) : -1); | ||
328 | int j2 = (y < 2*h ? dtype(i) : -1); | ||
329 | if (j1 != j2) | ||
330 | v = '-'; | ||
331 | else | ||
332 | v = dchar(j1); | ||
333 | } else { | ||
334 | int j1 = (x > 0 && y > 0 ? dtype(i-w-1) : -1); | ||
335 | int j2 = (x > 0 && y < 2*h ? dtype(i-1) : -1); | ||
336 | int j3 = (x < 2*w && y > 0 ? dtype(i-w) : -1); | ||
337 | int j4 = (x < 2*w && y < 2*h ? dtype(i) : -1); | ||
338 | if (j1 == j2 && j2 == j3 && j3 == j4) | ||
339 | v = dchar(j1); | ||
340 | else if (j1 == j2 && j3 == j4) | ||
341 | v = '|'; | ||
342 | else if (j1 == j3 && j2 == j4) | ||
343 | v = '-'; | ||
344 | else | ||
345 | v = '+'; | ||
346 | } | ||
347 | |||
348 | assert(retpos < retlen); | ||
349 | ret[retpos++] = v; | ||
350 | } | ||
351 | assert(retpos < retlen); | ||
352 | ret[retpos++] = '\n'; | ||
353 | } | ||
354 | assert(retpos < retlen); | ||
355 | ret[retpos++] = '\0'; | ||
356 | assert(retpos == retlen); | ||
357 | |||
358 | return ret; | ||
359 | } | ||
360 | |||
361 | /* ---------------------------------------------------------------------- | ||
362 | * Solver. | ||
363 | */ | ||
364 | |||
365 | /* | ||
366 | * During solver execution, the set of visited board positions is | ||
367 | * stored as a tree234 of the following structures. `w', `h' and | ||
368 | * `data' are obvious in meaning; `dist' represents the minimum | ||
369 | * distance to reach this position from the starting point. | ||
370 | * | ||
371 | * `prev' links each board to the board position from which it was | ||
372 | * most efficiently derived. | ||
373 | */ | ||
374 | struct board { | ||
375 | int w, h; | ||
376 | int dist; | ||
377 | struct board *prev; | ||
378 | unsigned char *data; | ||
379 | }; | ||
380 | |||
381 | static int boardcmp(void *av, void *bv) | ||
382 | { | ||
383 | struct board *a = (struct board *)av; | ||
384 | struct board *b = (struct board *)bv; | ||
385 | return memcmp(a->data, b->data, a->w * a->h); | ||
386 | } | ||
387 | |||
388 | static struct board *newboard(int w, int h, unsigned char *data) | ||
389 | { | ||
390 | struct board *b = malloc(sizeof(struct board) + w*h); | ||
391 | b->data = (unsigned char *)b + sizeof(struct board); | ||
392 | memcpy(b->data, data, w*h); | ||
393 | b->w = w; | ||
394 | b->h = h; | ||
395 | b->dist = -1; | ||
396 | b->prev = NULL; | ||
397 | return b; | ||
398 | } | ||
399 | |||
400 | /* | ||
401 | * The actual solver. Given a board, attempt to find the minimum | ||
402 | * length of move sequence which moves MAINANCHOR to (tx,ty), or | ||
403 | * -1 if no solution exists. Returns that minimum length. | ||
404 | * | ||
405 | * Also, if `moveout' is provided, writes out the moves in the | ||
406 | * form of a sequence of pairs of integers indicating the source | ||
407 | * and destination points of the anchor of the moved piece in each | ||
408 | * move. Exactly twice as many integers are written as the number | ||
409 | * returned from solve_board(), and `moveout' receives an int * | ||
410 | * which is a pointer to a dynamically allocated array. | ||
411 | */ | ||
412 | static int solve_board(int w, int h, unsigned char *board, | ||
413 | bool *forcefield, int tx, int ty, | ||
414 | int movelimit, int **moveout) | ||
415 | { | ||
416 | int wh = w*h; | ||
417 | struct board *b, *b2, *b3; | ||
418 | int *next, *which; | ||
419 | bool *anchors, *movereached; | ||
420 | int *movequeue, mqhead, mqtail; | ||
421 | tree234 *sorted, *queue; | ||
422 | int i, j, dir; | ||
423 | int qlen, lastdist; | ||
424 | int ret; | ||
425 | |||
426 | #ifdef SOLVER_DIAGNOSTICS | ||
427 | { | ||
428 | char *t = board_text_format(w, h, board); | ||
429 | for (i = 0; i < h; i++) { | ||
430 | for (j = 0; j < w; j++) { | ||
431 | int c = board[i*w+j]; | ||
432 | if (ISDIST(c)) | ||
433 | printf("D%-3d", c); | ||
434 | else if (c == MAINANCHOR) | ||
435 | printf("M "); | ||
436 | else if (c == ANCHOR) | ||
437 | printf("A "); | ||
438 | else if (c == WALL) | ||
439 | printf("W "); | ||
440 | else if (c == EMPTY) | ||
441 | printf("E "); | ||
442 | } | ||
443 | printf("\n"); | ||
444 | } | ||
445 | |||
446 | printf("Starting solver for:\n%s\n", t); | ||
447 | sfree(t); | ||
448 | } | ||
449 | #endif | ||
450 | |||
451 | sorted = newtree234(boardcmp); | ||
452 | queue = newtree234(NULL); | ||
453 | |||
454 | b = newboard(w, h, board); | ||
455 | b->dist = 0; | ||
456 | add234(sorted, b); | ||
457 | addpos234(queue, b, 0); | ||
458 | qlen = 1; | ||
459 | |||
460 | next = snewn(wh, int); | ||
461 | anchors = snewn(wh, bool); | ||
462 | which = snewn(wh, int); | ||
463 | movereached = snewn(wh, bool); | ||
464 | movequeue = snewn(wh, int); | ||
465 | lastdist = -1; | ||
466 | |||
467 | while ((b = delpos234(queue, 0)) != NULL) { | ||
468 | qlen--; | ||
469 | if (movelimit >= 0 && b->dist >= movelimit) { | ||
470 | /* | ||
471 | * The problem is not soluble in under `movelimit' | ||
472 | * moves, so we can quit right now. | ||
473 | */ | ||
474 | b2 = NULL; | ||
475 | goto done; | ||
476 | } | ||
477 | if (b->dist != lastdist) { | ||
478 | #ifdef SOLVER_DIAGNOSTICS | ||
479 | printf("dist %d (%d)\n", b->dist, count234(sorted)); | ||
480 | #endif | ||
481 | lastdist = b->dist; | ||
482 | } | ||
483 | /* | ||
484 | * Find all the anchors and form a linked list of the | ||
485 | * squares within each block. | ||
486 | */ | ||
487 | for (i = 0; i < wh; i++) { | ||
488 | next[i] = -1; | ||
489 | anchors[i] = false; | ||
490 | which[i] = -1; | ||
491 | if (ISANCHOR(b->data[i])) { | ||
492 | anchors[i] = true; | ||
493 | which[i] = i; | ||
494 | } else if (ISDIST(b->data[i])) { | ||
495 | j = i - b->data[i]; | ||
496 | next[j] = i; | ||
497 | which[i] = which[j]; | ||
498 | } | ||
499 | } | ||
500 | |||
501 | /* | ||
502 | * For each anchor, do an array-based BFS to find all the | ||
503 | * places we can slide it to. | ||
504 | */ | ||
505 | for (i = 0; i < wh; i++) { | ||
506 | if (!anchors[i]) | ||
507 | continue; | ||
508 | |||
509 | mqhead = mqtail = 0; | ||
510 | for (j = 0; j < wh; j++) | ||
511 | movereached[j] = false; | ||
512 | movequeue[mqtail++] = i; | ||
513 | while (mqhead < mqtail) { | ||
514 | int pos = movequeue[mqhead++]; | ||
515 | |||
516 | /* | ||
517 | * Try to move in each direction from here. | ||
518 | */ | ||
519 | for (dir = 0; dir < 4; dir++) { | ||
520 | int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0); | ||
521 | int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0); | ||
522 | int offset = dy*w + dx; | ||
523 | int newpos = pos + offset; | ||
524 | int d = newpos - i; | ||
525 | |||
526 | /* | ||
527 | * For each square involved in this block, | ||
528 | * check to see if the square d spaces away | ||
529 | * from it is either empty or part of the same | ||
530 | * block. | ||
531 | */ | ||
532 | for (j = i; j >= 0; j = next[j]) { | ||
533 | int jy = (pos+j-i) / w + dy, jx = (pos+j-i) % w + dx; | ||
534 | if (jy >= 0 && jy < h && jx >= 0 && jx < w && | ||
535 | ((b->data[j+d] == EMPTY || which[j+d] == i) && | ||
536 | (b->data[i] == MAINANCHOR || !forcefield[j+d]))) | ||
537 | /* ok */; | ||
538 | else | ||
539 | break; | ||
540 | } | ||
541 | if (j >= 0) | ||
542 | continue; /* this direction wasn't feasible */ | ||
543 | |||
544 | /* | ||
545 | * If we've already tried moving this piece | ||
546 | * here, leave it. | ||
547 | */ | ||
548 | if (movereached[newpos]) | ||
549 | continue; | ||
550 | movereached[newpos] = true; | ||
551 | movequeue[mqtail++] = newpos; | ||
552 | |||
553 | /* | ||
554 | * We have a viable move. Make it. | ||
555 | */ | ||
556 | b2 = newboard(w, h, b->data); | ||
557 | for (j = i; j >= 0; j = next[j]) | ||
558 | b2->data[j] = EMPTY; | ||
559 | for (j = i; j >= 0; j = next[j]) | ||
560 | b2->data[j+d] = b->data[j]; | ||
561 | |||
562 | b3 = add234(sorted, b2); | ||
563 | if (b3 != b2) { | ||
564 | sfree(b2); /* we already got one */ | ||
565 | } else { | ||
566 | b2->dist = b->dist + 1; | ||
567 | b2->prev = b; | ||
568 | addpos234(queue, b2, qlen++); | ||
569 | if (b2->data[ty*w+tx] == MAINANCHOR) | ||
570 | goto done; /* search completed! */ | ||
571 | } | ||
572 | } | ||
573 | } | ||
574 | } | ||
575 | } | ||
576 | b2 = NULL; | ||
577 | |||
578 | done: | ||
579 | |||
580 | if (b2) { | ||
581 | ret = b2->dist; | ||
582 | if (moveout) { | ||
583 | /* | ||
584 | * Now b2 represents the solved position. Backtrack to | ||
585 | * output the solution. | ||
586 | */ | ||
587 | *moveout = snewn(ret * 2, int); | ||
588 | j = ret * 2; | ||
589 | |||
590 | while (b2->prev) { | ||
591 | int from = -1, to = -1; | ||
592 | |||
593 | b = b2->prev; | ||
594 | |||
595 | /* | ||
596 | * Scan b and b2 to find out which piece has | ||
597 | * moved. | ||
598 | */ | ||
599 | for (i = 0; i < wh; i++) { | ||
600 | if (ISANCHOR(b->data[i]) && !ISANCHOR(b2->data[i])) { | ||
601 | assert(from == -1); | ||
602 | from = i; | ||
603 | } else if (!ISANCHOR(b->data[i]) && ISANCHOR(b2->data[i])){ | ||
604 | assert(to == -1); | ||
605 | to = i; | ||
606 | } | ||
607 | } | ||
608 | |||
609 | assert(from >= 0 && to >= 0); | ||
610 | assert(j >= 2); | ||
611 | (*moveout)[--j] = to; | ||
612 | (*moveout)[--j] = from; | ||
613 | |||
614 | b2 = b; | ||
615 | } | ||
616 | assert(j == 0); | ||
617 | } | ||
618 | } else { | ||
619 | ret = -1; /* no solution */ | ||
620 | if (moveout) | ||
621 | *moveout = NULL; | ||
622 | } | ||
623 | |||
624 | freetree234(queue); | ||
625 | |||
626 | while ((b = delpos234(sorted, 0)) != NULL) | ||
627 | sfree(b); | ||
628 | freetree234(sorted); | ||
629 | |||
630 | sfree(next); | ||
631 | sfree(anchors); | ||
632 | sfree(movereached); | ||
633 | sfree(movequeue); | ||
634 | sfree(which); | ||
635 | |||
636 | return ret; | ||
637 | } | ||
638 | |||
639 | /* ---------------------------------------------------------------------- | ||
640 | * Random board generation. | ||
641 | */ | ||
642 | |||
643 | static void generate_board(int w, int h, int *rtx, int *rty, int *minmoves, | ||
644 | random_state *rs, unsigned char **rboard, | ||
645 | bool **rforcefield, int movelimit) | ||
646 | { | ||
647 | int wh = w*h; | ||
648 | unsigned char *board, *board2; | ||
649 | bool *forcefield; | ||
650 | bool *tried_merge; | ||
651 | DSF *dsf; | ||
652 | int *list, nlist, pos; | ||
653 | int tx, ty; | ||
654 | int i, j; | ||
655 | int moves = 0; /* placate optimiser */ | ||
656 | |||
657 | /* | ||
658 | * Set up a board and fill it with singletons, except for a | ||
659 | * border of walls. | ||
660 | */ | ||
661 | board = snewn(wh, unsigned char); | ||
662 | forcefield = snewn(wh, bool); | ||
663 | board2 = snewn(wh, unsigned char); | ||
664 | memset(board, ANCHOR, wh); | ||
665 | memset(forcefield, 0, wh * sizeof(bool)); | ||
666 | for (i = 0; i < w; i++) | ||
667 | board[i] = board[i+w*(h-1)] = WALL; | ||
668 | for (i = 0; i < h; i++) | ||
669 | board[i*w] = board[i*w+(w-1)] = WALL; | ||
670 | |||
671 | tried_merge = snewn(wh * wh, bool); | ||
672 | memset(tried_merge, 0, wh*wh * sizeof(bool)); | ||
673 | dsf = dsf_new(wh); | ||
674 | |||
675 | /* | ||
676 | * Invent a main piece at one extreme. (FIXME: vary the | ||
677 | * extreme, and the piece.) | ||
678 | */ | ||
679 | board[w+1] = MAINANCHOR; | ||
680 | board[w+2] = DIST(1); | ||
681 | board[w*2+1] = DIST(w-1); | ||
682 | board[w*2+2] = DIST(1); | ||
683 | |||
684 | /* | ||
685 | * Invent a target position. (FIXME: vary this too.) | ||
686 | */ | ||
687 | tx = w-2; | ||
688 | ty = h-3; | ||
689 | forcefield[ty*w+tx+1] = true; | ||
690 | forcefield[(ty+1)*w+tx+1] = true; | ||
691 | board[ty*w+tx+1] = board[(ty+1)*w+tx+1] = EMPTY; | ||
692 | |||
693 | /* | ||
694 | * Gradually remove singletons until the game becomes soluble. | ||
695 | */ | ||
696 | for (j = w; j-- > 0 ;) | ||
697 | for (i = h; i-- > 0 ;) | ||
698 | if (board[i*w+j] == ANCHOR) { | ||
699 | /* | ||
700 | * See if the board is already soluble. | ||
701 | */ | ||
702 | if ((moves = solve_board(w, h, board, forcefield, | ||
703 | tx, ty, movelimit, NULL)) >= 0) | ||
704 | goto soluble; | ||
705 | |||
706 | /* | ||
707 | * Otherwise, remove this piece. | ||
708 | */ | ||
709 | board[i*w+j] = EMPTY; | ||
710 | } | ||
711 | assert(!"We shouldn't get here"); | ||
712 | soluble: | ||
713 | |||
714 | /* | ||
715 | * Make a list of all the inter-block edges on the board. | ||
716 | */ | ||
717 | list = snewn(wh*2, int); | ||
718 | nlist = 0; | ||
719 | for (i = 0; i+1 < w; i++) | ||
720 | for (j = 0; j < h; j++) | ||
721 | list[nlist++] = (j*w+i) * 2 + 0; /* edge to the right of j*w+i */ | ||
722 | for (j = 0; j+1 < h; j++) | ||
723 | for (i = 0; i < w; i++) | ||
724 | list[nlist++] = (j*w+i) * 2 + 1; /* edge below j*w+i */ | ||
725 | |||
726 | /* | ||
727 | * Now go through that list in random order, trying to merge | ||
728 | * the blocks on each side of each edge. | ||
729 | */ | ||
730 | shuffle(list, nlist, sizeof(*list), rs); | ||
731 | while (nlist > 0) { | ||
732 | int x1, y1, p1, c1; | ||
733 | int x2, y2, p2, c2; | ||
734 | |||
735 | pos = list[--nlist]; | ||
736 | y1 = y2 = pos / (w*2); | ||
737 | x1 = x2 = (pos / 2) % w; | ||
738 | if (pos % 2) | ||
739 | y2++; | ||
740 | else | ||
741 | x2++; | ||
742 | p1 = y1*w+x1; | ||
743 | p2 = y2*w+x2; | ||
744 | |||
745 | /* | ||
746 | * Immediately abandon the attempt if we've already tried | ||
747 | * to merge the same pair of blocks along a different | ||
748 | * edge. | ||
749 | */ | ||
750 | c1 = dsf_canonify(dsf, p1); | ||
751 | c2 = dsf_canonify(dsf, p2); | ||
752 | if (tried_merge[c1 * wh + c2]) | ||
753 | continue; | ||
754 | |||
755 | /* | ||
756 | * In order to be mergeable, these two squares must each | ||
757 | * either be, or belong to, a non-main anchor, and their | ||
758 | * anchors must also be distinct. | ||
759 | */ | ||
760 | if (!ISBLOCK(board[p1]) || !ISBLOCK(board[p2])) | ||
761 | continue; | ||
762 | while (ISDIST(board[p1])) | ||
763 | p1 -= board[p1]; | ||
764 | while (ISDIST(board[p2])) | ||
765 | p2 -= board[p2]; | ||
766 | if (board[p1] == MAINANCHOR || board[p2] == MAINANCHOR || p1 == p2) | ||
767 | continue; | ||
768 | |||
769 | /* | ||
770 | * We can merge these blocks. Try it, and see if the | ||
771 | * puzzle remains soluble. | ||
772 | */ | ||
773 | memcpy(board2, board, wh); | ||
774 | j = -1; | ||
775 | while (p1 < wh || p2 < wh) { | ||
776 | /* | ||
777 | * p1 and p2 are the squares at the head of each block | ||
778 | * list. Pick the smaller one and put it on the output | ||
779 | * block list. | ||
780 | */ | ||
781 | i = min(p1, p2); | ||
782 | if (j < 0) { | ||
783 | board[i] = ANCHOR; | ||
784 | } else { | ||
785 | assert(i - j <= MAXDIST); | ||
786 | board[i] = DIST(i - j); | ||
787 | } | ||
788 | j = i; | ||
789 | |||
790 | /* | ||
791 | * Now advance whichever list that came from. | ||
792 | */ | ||
793 | if (i == p1) { | ||
794 | do { | ||
795 | p1++; | ||
796 | } while (p1 < wh && board[p1] != DIST(p1-i)); | ||
797 | } else { | ||
798 | do { | ||
799 | p2++; | ||
800 | } while (p2 < wh && board[p2] != DIST(p2-i)); | ||
801 | } | ||
802 | } | ||
803 | j = solve_board(w, h, board, forcefield, tx, ty, movelimit, NULL); | ||
804 | if (j < 0) { | ||
805 | /* | ||
806 | * Didn't work. Revert the merge. | ||
807 | */ | ||
808 | memcpy(board, board2, wh); | ||
809 | tried_merge[c1 * wh + c2] = true; | ||
810 | tried_merge[c2 * wh + c1] = true; | ||
811 | } else { | ||
812 | int c; | ||
813 | |||
814 | moves = j; | ||
815 | |||
816 | dsf_merge(dsf, c1, c2); | ||
817 | c = dsf_canonify(dsf, c1); | ||
818 | for (i = 0; i < wh; i++) | ||
819 | tried_merge[c*wh+i] = (tried_merge[c1*wh+i] || | ||
820 | tried_merge[c2*wh+i]); | ||
821 | for (i = 0; i < wh; i++) | ||
822 | tried_merge[i*wh+c] = (tried_merge[i*wh+c1] || | ||
823 | tried_merge[i*wh+c2]); | ||
824 | } | ||
825 | } | ||
826 | |||
827 | dsf_free(dsf); | ||
828 | sfree(list); | ||
829 | sfree(tried_merge); | ||
830 | sfree(board2); | ||
831 | |||
832 | *rtx = tx; | ||
833 | *rty = ty; | ||
834 | *rboard = board; | ||
835 | *rforcefield = forcefield; | ||
836 | *minmoves = moves; | ||
837 | } | ||
838 | |||
839 | /* ---------------------------------------------------------------------- | ||
840 | * End of solver/generator code. | ||
841 | */ | ||
842 | |||
843 | static char *new_game_desc(const game_params *params, random_state *rs, | ||
844 | char **aux, bool interactive) | ||
845 | { | ||
846 | int w = params->w, h = params->h, wh = w*h; | ||
847 | int tx, ty, minmoves; | ||
848 | unsigned char *board; | ||
849 | bool *forcefield; | ||
850 | char *ret, *p; | ||
851 | int i; | ||
852 | |||
853 | generate_board(params->w, params->h, &tx, &ty, &minmoves, rs, | ||
854 | &board, &forcefield, params->maxmoves); | ||
855 | #ifdef GENERATOR_DIAGNOSTICS | ||
856 | { | ||
857 | char *t = board_text_format(params->w, params->h, board); | ||
858 | printf("%s\n", t); | ||
859 | sfree(t); | ||
860 | } | ||
861 | #endif | ||
862 | |||
863 | /* | ||
864 | * Encode as a game ID. | ||
865 | */ | ||
866 | ret = snewn(wh * 6 + 40, char); | ||
867 | p = ret; | ||
868 | i = 0; | ||
869 | while (i < wh) { | ||
870 | if (ISDIST(board[i])) { | ||
871 | p += sprintf(p, "d%d", board[i]); | ||
872 | i++; | ||
873 | } else { | ||
874 | int count = 1; | ||
875 | int b = board[i]; | ||
876 | bool f = forcefield[i]; | ||
877 | int c = (b == ANCHOR ? 'a' : | ||
878 | b == MAINANCHOR ? 'm' : | ||
879 | b == EMPTY ? 'e' : | ||
880 | /* b == WALL ? */ 'w'); | ||
881 | if (f) *p++ = 'f'; | ||
882 | *p++ = c; | ||
883 | i++; | ||
884 | while (i < wh && board[i] == b && forcefield[i] == f) | ||
885 | i++, count++; | ||
886 | if (count > 1) | ||
887 | p += sprintf(p, "%d", count); | ||
888 | } | ||
889 | } | ||
890 | p += sprintf(p, ",%d,%d,%d", tx, ty, minmoves); | ||
891 | ret = sresize(ret, p+1 - ret, char); | ||
892 | |||
893 | sfree(board); | ||
894 | sfree(forcefield); | ||
895 | |||
896 | return ret; | ||
897 | } | ||
898 | |||
899 | static const char *validate_desc(const game_params *params, const char *desc) | ||
900 | { | ||
901 | int w = params->w, h = params->h, wh = w*h; | ||
902 | bool *active; | ||
903 | int *link; | ||
904 | int mains = 0; | ||
905 | int i, tx, ty, minmoves; | ||
906 | const char *ret; | ||
907 | |||
908 | active = snewn(wh, bool); | ||
909 | link = snewn(wh, int); | ||
910 | i = 0; | ||
911 | |||
912 | while (*desc && *desc != ',') { | ||
913 | if (i >= wh) { | ||
914 | ret = "Too much data in game description"; | ||
915 | goto done; | ||
916 | } | ||
917 | link[i] = -1; | ||
918 | active[i] = false; | ||
919 | if (*desc == 'f' || *desc == 'F') { | ||
920 | desc++; | ||
921 | if (!*desc) { | ||
922 | ret = "Expected another character after 'f' in game " | ||
923 | "description"; | ||
924 | goto done; | ||
925 | } | ||
926 | } | ||
927 | |||
928 | if (*desc == 'd' || *desc == 'D') { | ||
929 | int dist; | ||
930 | |||
931 | desc++; | ||
932 | if (!isdigit((unsigned char)*desc)) { | ||
933 | ret = "Expected a number after 'd' in game description"; | ||
934 | goto done; | ||
935 | } | ||
936 | dist = atoi(desc); | ||
937 | while (*desc && isdigit((unsigned char)*desc)) desc++; | ||
938 | |||
939 | if (dist <= 0 || dist > i) { | ||
940 | ret = "Out-of-range number after 'd' in game description"; | ||
941 | goto done; | ||
942 | } | ||
943 | |||
944 | if (!active[i - dist]) { | ||
945 | ret = "Invalid back-reference in game description"; | ||
946 | goto done; | ||
947 | } | ||
948 | |||
949 | link[i] = i - dist; | ||
950 | |||
951 | active[i] = true; | ||
952 | active[link[i]] = false; | ||
953 | i++; | ||
954 | } else { | ||
955 | int c = *desc++; | ||
956 | int count = 1; | ||
957 | |||
958 | if (!strchr("aAmMeEwW", c)) { | ||
959 | ret = "Invalid character in game description"; | ||
960 | goto done; | ||
961 | } | ||
962 | if (isdigit((unsigned char)*desc)) { | ||
963 | count = atoi(desc); | ||
964 | while (*desc && isdigit((unsigned char)*desc)) desc++; | ||
965 | } | ||
966 | if (i + count > wh) { | ||
967 | ret = "Too much data in game description"; | ||
968 | goto done; | ||
969 | } | ||
970 | while (count-- > 0) { | ||
971 | active[i] = (strchr("aAmM", c) != NULL); | ||
972 | link[i] = -1; | ||
973 | if (strchr("mM", c) != NULL) { | ||
974 | mains++; | ||
975 | } | ||
976 | i++; | ||
977 | } | ||
978 | } | ||
979 | } | ||
980 | if (mains != 1) { | ||
981 | ret = (mains == 0 ? "No main piece specified in game description" : | ||
982 | "More than one main piece specified in game description"); | ||
983 | goto done; | ||
984 | } | ||
985 | if (i < wh) { | ||
986 | ret = "Not enough data in game description"; | ||
987 | goto done; | ||
988 | } | ||
989 | |||
990 | /* | ||
991 | * Now read the target coordinates. | ||
992 | */ | ||
993 | i = sscanf(desc, ",%d,%d,%d", &tx, &ty, &minmoves); | ||
994 | if (i < 2) { | ||
995 | ret = "No target coordinates specified"; | ||
996 | goto done; | ||
997 | /* | ||
998 | * (but minmoves is optional) | ||
999 | */ | ||
1000 | } | ||
1001 | |||
1002 | ret = NULL; | ||
1003 | |||
1004 | done: | ||
1005 | sfree(active); | ||
1006 | sfree(link); | ||
1007 | return ret; | ||
1008 | } | ||
1009 | |||
1010 | static game_state *new_game(midend *me, const game_params *params, | ||
1011 | const char *desc) | ||
1012 | { | ||
1013 | int w = params->w, h = params->h, wh = w*h; | ||
1014 | game_state *state; | ||
1015 | int i; | ||
1016 | |||
1017 | state = snew(game_state); | ||
1018 | state->w = w; | ||
1019 | state->h = h; | ||
1020 | state->board = snewn(wh, unsigned char); | ||
1021 | state->lastmoved = state->lastmoved_pos = -1; | ||
1022 | state->movecount = 0; | ||
1023 | state->imm = snew(struct game_immutable_state); | ||
1024 | state->imm->refcount = 1; | ||
1025 | state->imm->forcefield = snewn(wh, bool); | ||
1026 | |||
1027 | i = 0; | ||
1028 | |||
1029 | while (*desc && *desc != ',') { | ||
1030 | bool f = false; | ||
1031 | |||
1032 | assert(i < wh); | ||
1033 | |||
1034 | if (*desc == 'f') { | ||
1035 | f = true; | ||
1036 | desc++; | ||
1037 | assert(*desc); | ||
1038 | } | ||
1039 | |||
1040 | if (*desc == 'd' || *desc == 'D') { | ||
1041 | int dist; | ||
1042 | |||
1043 | desc++; | ||
1044 | dist = atoi(desc); | ||
1045 | while (*desc && isdigit((unsigned char)*desc)) desc++; | ||
1046 | |||
1047 | state->board[i] = DIST(dist); | ||
1048 | state->imm->forcefield[i] = f; | ||
1049 | |||
1050 | i++; | ||
1051 | } else { | ||
1052 | int c = *desc++; | ||
1053 | int count = 1; | ||
1054 | |||
1055 | if (isdigit((unsigned char)*desc)) { | ||
1056 | count = atoi(desc); | ||
1057 | while (*desc && isdigit((unsigned char)*desc)) desc++; | ||
1058 | } | ||
1059 | assert(i + count <= wh); | ||
1060 | |||
1061 | c = (c == 'a' || c == 'A' ? ANCHOR : | ||
1062 | c == 'm' || c == 'M' ? MAINANCHOR : | ||
1063 | c == 'e' || c == 'E' ? EMPTY : | ||
1064 | /* c == 'w' || c == 'W' ? */ WALL); | ||
1065 | |||
1066 | while (count-- > 0) { | ||
1067 | state->board[i] = c; | ||
1068 | state->imm->forcefield[i] = f; | ||
1069 | i++; | ||
1070 | } | ||
1071 | } | ||
1072 | } | ||
1073 | |||
1074 | /* | ||
1075 | * Now read the target coordinates. | ||
1076 | */ | ||
1077 | state->tx = state->ty = 0; | ||
1078 | state->minmoves = -1; | ||
1079 | i = sscanf(desc, ",%d,%d,%d", &state->tx, &state->ty, &state->minmoves); | ||
1080 | |||
1081 | if (state->board[state->ty*w+state->tx] == MAINANCHOR) | ||
1082 | state->completed = 0; /* already complete! */ | ||
1083 | else | ||
1084 | state->completed = -1; | ||
1085 | |||
1086 | state->cheated = false; | ||
1087 | state->soln = NULL; | ||
1088 | state->soln_index = -1; | ||
1089 | |||
1090 | return state; | ||
1091 | } | ||
1092 | |||
1093 | static game_state *dup_game(const game_state *state) | ||
1094 | { | ||
1095 | int w = state->w, h = state->h, wh = w*h; | ||
1096 | game_state *ret = snew(game_state); | ||
1097 | |||
1098 | ret->w = state->w; | ||
1099 | ret->h = state->h; | ||
1100 | ret->board = snewn(wh, unsigned char); | ||
1101 | memcpy(ret->board, state->board, wh); | ||
1102 | ret->tx = state->tx; | ||
1103 | ret->ty = state->ty; | ||
1104 | ret->minmoves = state->minmoves; | ||
1105 | ret->lastmoved = state->lastmoved; | ||
1106 | ret->lastmoved_pos = state->lastmoved_pos; | ||
1107 | ret->movecount = state->movecount; | ||
1108 | ret->completed = state->completed; | ||
1109 | ret->cheated = state->cheated; | ||
1110 | ret->imm = state->imm; | ||
1111 | ret->imm->refcount++; | ||
1112 | ret->soln = state->soln; | ||
1113 | ret->soln_index = state->soln_index; | ||
1114 | if (ret->soln) | ||
1115 | ret->soln->refcount++; | ||
1116 | |||
1117 | return ret; | ||
1118 | } | ||
1119 | |||
1120 | static void free_game(game_state *state) | ||
1121 | { | ||
1122 | if (--state->imm->refcount <= 0) { | ||
1123 | sfree(state->imm->forcefield); | ||
1124 | sfree(state->imm); | ||
1125 | } | ||
1126 | if (state->soln && --state->soln->refcount <= 0) { | ||
1127 | sfree(state->soln->moves); | ||
1128 | sfree(state->soln); | ||
1129 | } | ||
1130 | sfree(state->board); | ||
1131 | sfree(state); | ||
1132 | } | ||
1133 | |||
1134 | static char *solve_game(const game_state *state, const game_state *currstate, | ||
1135 | const char *aux, const char **error) | ||
1136 | { | ||
1137 | int *moves; | ||
1138 | int nmoves; | ||
1139 | int i; | ||
1140 | char *ret, *p, sep; | ||
1141 | |||
1142 | /* | ||
1143 | * Run the solver and attempt to find the shortest solution | ||
1144 | * from the current position. | ||
1145 | */ | ||
1146 | nmoves = solve_board(state->w, state->h, state->board, | ||
1147 | state->imm->forcefield, state->tx, state->ty, | ||
1148 | -1, &moves); | ||
1149 | |||
1150 | if (nmoves < 0) { | ||
1151 | *error = "Unable to find a solution to this puzzle"; | ||
1152 | return NULL; | ||
1153 | } | ||
1154 | if (nmoves == 0) { | ||
1155 | *error = "Puzzle is already solved"; | ||
1156 | return NULL; | ||
1157 | } | ||
1158 | |||
1159 | /* | ||
1160 | * Encode the resulting solution as a move string. | ||
1161 | */ | ||
1162 | ret = snewn(nmoves * 40, char); | ||
1163 | p = ret; | ||
1164 | sep = 'S'; | ||
1165 | |||
1166 | for (i = 0; i < nmoves; i++) { | ||
1167 | p += sprintf(p, "%c%d-%d", sep, moves[i*2], moves[i*2+1]); | ||
1168 | sep = ','; | ||
1169 | } | ||
1170 | |||
1171 | sfree(moves); | ||
1172 | assert(p - ret < nmoves * 40); | ||
1173 | ret = sresize(ret, p+1 - ret, char); | ||
1174 | |||
1175 | return ret; | ||
1176 | } | ||
1177 | |||
1178 | static bool game_can_format_as_text_now(const game_params *params) | ||
1179 | { | ||
1180 | return true; | ||
1181 | } | ||
1182 | |||
1183 | static char *game_text_format(const game_state *state) | ||
1184 | { | ||
1185 | return board_text_format(state->w, state->h, state->board, | ||
1186 | state->imm->forcefield); | ||
1187 | } | ||
1188 | |||
1189 | struct game_ui { | ||
1190 | bool dragging; | ||
1191 | int drag_anchor; | ||
1192 | int drag_offset_x, drag_offset_y; | ||
1193 | int drag_currpos; | ||
1194 | bool *reachable; | ||
1195 | int *bfs_queue; /* used as scratch in interpret_move */ | ||
1196 | }; | ||
1197 | |||
1198 | static game_ui *new_ui(const game_state *state) | ||
1199 | { | ||
1200 | int w = state->w, h = state->h, wh = w*h; | ||
1201 | game_ui *ui = snew(game_ui); | ||
1202 | |||
1203 | ui->dragging = false; | ||
1204 | ui->drag_anchor = ui->drag_currpos = -1; | ||
1205 | ui->drag_offset_x = ui->drag_offset_y = -1; | ||
1206 | ui->reachable = snewn(wh, bool); | ||
1207 | memset(ui->reachable, 0, wh * sizeof(bool)); | ||
1208 | ui->bfs_queue = snewn(wh, int); | ||
1209 | |||
1210 | return ui; | ||
1211 | } | ||
1212 | |||
1213 | static void free_ui(game_ui *ui) | ||
1214 | { | ||
1215 | sfree(ui->bfs_queue); | ||
1216 | sfree(ui->reachable); | ||
1217 | sfree(ui); | ||
1218 | } | ||
1219 | |||
1220 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | ||
1221 | const game_state *newstate) | ||
1222 | { | ||
1223 | } | ||
1224 | |||
1225 | #define PREFERRED_TILESIZE 32 | ||
1226 | #define TILESIZE (ds->tilesize) | ||
1227 | #define BORDER (TILESIZE/2) | ||
1228 | #define COORD(x) ( (x) * TILESIZE + BORDER ) | ||
1229 | #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) | ||
1230 | #define BORDER_WIDTH (1 + TILESIZE/20) | ||
1231 | #define HIGHLIGHT_WIDTH (1 + TILESIZE/16) | ||
1232 | |||
1233 | #define FLASH_INTERVAL 0.10F | ||
1234 | #define FLASH_TIME 3*FLASH_INTERVAL | ||
1235 | |||
1236 | struct game_drawstate { | ||
1237 | int tilesize; | ||
1238 | int w, h; | ||
1239 | unsigned long *grid; /* what's currently displayed */ | ||
1240 | }; | ||
1241 | |||
1242 | static char *interpret_move(const game_state *state, game_ui *ui, | ||
1243 | const game_drawstate *ds, | ||
1244 | int x, int y, int button) | ||
1245 | { | ||
1246 | int w = state->w, h = state->h, wh = w*h; | ||
1247 | int tx, ty, i, j; | ||
1248 | int qhead, qtail; | ||
1249 | |||
1250 | if (button == LEFT_BUTTON) { | ||
1251 | tx = FROMCOORD(x); | ||
1252 | ty = FROMCOORD(y); | ||
1253 | |||
1254 | if (tx < 0 || tx >= w || ty < 0 || ty >= h || | ||
1255 | !ISBLOCK(state->board[ty*w+tx])) | ||
1256 | return NULL; /* this click has no effect */ | ||
1257 | |||
1258 | /* | ||
1259 | * User has clicked on a block. Find the block's anchor | ||
1260 | * and register that we've started dragging it. | ||
1261 | */ | ||
1262 | i = ty*w+tx; | ||
1263 | while (ISDIST(state->board[i])) | ||
1264 | i -= state->board[i]; | ||
1265 | assert(i >= 0 && i < wh); | ||
1266 | |||
1267 | ui->dragging = true; | ||
1268 | ui->drag_anchor = i; | ||
1269 | ui->drag_offset_x = tx - (i % w); | ||
1270 | ui->drag_offset_y = ty - (i / w); | ||
1271 | ui->drag_currpos = i; | ||
1272 | |||
1273 | /* | ||
1274 | * Now we immediately bfs out from the current location of | ||
1275 | * the anchor, to find all the places to which this block | ||
1276 | * can be dragged. | ||
1277 | */ | ||
1278 | memset(ui->reachable, 0, wh * sizeof(bool)); | ||
1279 | qhead = qtail = 0; | ||
1280 | ui->reachable[i] = true; | ||
1281 | ui->bfs_queue[qtail++] = i; | ||
1282 | for (j = i; j < wh; j++) | ||
1283 | if (state->board[j] == DIST(j - i)) | ||
1284 | i = j; | ||
1285 | while (qhead < qtail) { | ||
1286 | int pos = ui->bfs_queue[qhead++]; | ||
1287 | int x = pos % w, y = pos / w; | ||
1288 | int dir; | ||
1289 | |||
1290 | for (dir = 0; dir < 4; dir++) { | ||
1291 | int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0); | ||
1292 | int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0); | ||
1293 | int newpos; | ||
1294 | |||
1295 | if (x + dx < 0 || x + dx >= w || | ||
1296 | y + dy < 0 || y + dy >= h) | ||
1297 | continue; | ||
1298 | |||
1299 | newpos = pos + dy*w + dx; | ||
1300 | if (ui->reachable[newpos]) | ||
1301 | continue; /* already done this one */ | ||
1302 | |||
1303 | /* | ||
1304 | * Now search the grid to see if the block we're | ||
1305 | * dragging could fit into this space. | ||
1306 | */ | ||
1307 | for (j = i; j >= 0; j = (ISDIST(state->board[j]) ? | ||
1308 | j - state->board[j] : -1)) { | ||
1309 | int jx = (j+pos-ui->drag_anchor) % w; | ||
1310 | int jy = (j+pos-ui->drag_anchor) / w; | ||
1311 | int j2; | ||
1312 | |||
1313 | if (jx + dx < 0 || jx + dx >= w || | ||
1314 | jy + dy < 0 || jy + dy >= h) | ||
1315 | break; /* this position isn't valid at all */ | ||
1316 | |||
1317 | j2 = (j+pos-ui->drag_anchor) + dy*w + dx; | ||
1318 | |||
1319 | if (state->board[j2] == EMPTY && | ||
1320 | (!state->imm->forcefield[j2] || | ||
1321 | state->board[ui->drag_anchor] == MAINANCHOR)) | ||
1322 | continue; | ||
1323 | while (ISDIST(state->board[j2])) | ||
1324 | j2 -= state->board[j2]; | ||
1325 | assert(j2 >= 0 && j2 < wh); | ||
1326 | if (j2 == ui->drag_anchor) | ||
1327 | continue; | ||
1328 | else | ||
1329 | break; | ||
1330 | } | ||
1331 | |||
1332 | if (j < 0) { | ||
1333 | /* | ||
1334 | * If we got to the end of that loop without | ||
1335 | * disqualifying this position, mark it as | ||
1336 | * reachable for this drag. | ||
1337 | */ | ||
1338 | ui->reachable[newpos] = true; | ||
1339 | ui->bfs_queue[qtail++] = newpos; | ||
1340 | } | ||
1341 | } | ||
1342 | } | ||
1343 | |||
1344 | /* | ||
1345 | * And that's it. Update the display to reflect the start | ||
1346 | * of a drag. | ||
1347 | */ | ||
1348 | return MOVE_UI_UPDATE; | ||
1349 | } else if (button == LEFT_DRAG && ui->dragging) { | ||
1350 | int dist, distlimit, dx, dy, s, px, py; | ||
1351 | |||
1352 | tx = FROMCOORD(x); | ||
1353 | ty = FROMCOORD(y); | ||
1354 | |||
1355 | tx -= ui->drag_offset_x; | ||
1356 | ty -= ui->drag_offset_y; | ||
1357 | |||
1358 | /* | ||
1359 | * Now search outwards from (tx,ty), in order of Manhattan | ||
1360 | * distance, until we find a reachable square. | ||
1361 | */ | ||
1362 | distlimit = w+tx; | ||
1363 | distlimit = max(distlimit, h+ty); | ||
1364 | distlimit = max(distlimit, tx); | ||
1365 | distlimit = max(distlimit, ty); | ||
1366 | for (dist = 0; dist <= distlimit; dist++) { | ||
1367 | for (dx = -dist; dx <= dist; dx++) | ||
1368 | for (s = -1; s <= +1; s += 2) { | ||
1369 | dy = s * (dist - abs(dx)); | ||
1370 | px = tx + dx; | ||
1371 | py = ty + dy; | ||
1372 | if (px >= 0 && px < w && py >= 0 && py < h && | ||
1373 | ui->reachable[py*w+px]) { | ||
1374 | ui->drag_currpos = py*w+px; | ||
1375 | return MOVE_UI_UPDATE; | ||
1376 | } | ||
1377 | } | ||
1378 | } | ||
1379 | return NULL; /* give up - this drag has no effect */ | ||
1380 | } else if (button == LEFT_RELEASE && ui->dragging) { | ||
1381 | char data[256], *str; | ||
1382 | |||
1383 | /* | ||
1384 | * Terminate the drag, and if the piece has actually moved | ||
1385 | * then return a move string quoting the old and new | ||
1386 | * locations of the piece's anchor. | ||
1387 | */ | ||
1388 | if (ui->drag_anchor != ui->drag_currpos) { | ||
1389 | sprintf(data, "M%d-%d", ui->drag_anchor, ui->drag_currpos); | ||
1390 | str = dupstr(data); | ||
1391 | } else | ||
1392 | str = MOVE_UI_UPDATE; | ||
1393 | |||
1394 | ui->dragging = false; | ||
1395 | ui->drag_anchor = ui->drag_currpos = -1; | ||
1396 | ui->drag_offset_x = ui->drag_offset_y = -1; | ||
1397 | memset(ui->reachable, 0, wh * sizeof(bool)); | ||
1398 | |||
1399 | return str; | ||
1400 | } else if (button == ' ' && state->soln) { | ||
1401 | /* | ||
1402 | * Make the next move in the stored solution. | ||
1403 | */ | ||
1404 | char data[256]; | ||
1405 | int a1, a2; | ||
1406 | |||
1407 | a1 = state->soln->moves[state->soln_index*2]; | ||
1408 | a2 = state->soln->moves[state->soln_index*2+1]; | ||
1409 | if (a1 == state->lastmoved_pos) | ||
1410 | a1 = state->lastmoved; | ||
1411 | |||
1412 | sprintf(data, "M%d-%d", a1, a2); | ||
1413 | return dupstr(data); | ||
1414 | } | ||
1415 | |||
1416 | return NULL; | ||
1417 | } | ||
1418 | |||
1419 | static bool move_piece(int w, int h, const unsigned char *src, | ||
1420 | unsigned char *dst, bool *ff, int from, int to) | ||
1421 | { | ||
1422 | int wh = w*h; | ||
1423 | int i, j; | ||
1424 | |||
1425 | if (!ISANCHOR(dst[from])) | ||
1426 | return false; | ||
1427 | |||
1428 | /* | ||
1429 | * Scan to the far end of the piece's linked list. | ||
1430 | */ | ||
1431 | for (i = j = from; j < wh; j++) | ||
1432 | if (src[j] == DIST(j - i)) | ||
1433 | i = j; | ||
1434 | |||
1435 | /* | ||
1436 | * Remove the piece from its old location in the new | ||
1437 | * game state. | ||
1438 | */ | ||
1439 | for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1)) | ||
1440 | dst[j] = EMPTY; | ||
1441 | |||
1442 | /* | ||
1443 | * And put it back in at the new location. | ||
1444 | */ | ||
1445 | for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1)) { | ||
1446 | int jn = j + to - from; | ||
1447 | if (jn < 0 || jn >= wh) | ||
1448 | return false; | ||
1449 | if (dst[jn] == EMPTY && (!ff[jn] || src[from] == MAINANCHOR)) { | ||
1450 | dst[jn] = src[j]; | ||
1451 | } else { | ||
1452 | return false; | ||
1453 | } | ||
1454 | } | ||
1455 | |||
1456 | return true; | ||
1457 | } | ||
1458 | |||
1459 | static game_state *execute_move(const game_state *state, const char *move) | ||
1460 | { | ||
1461 | int w = state->w, h = state->h /* , wh = w*h */; | ||
1462 | char c; | ||
1463 | int a1, a2, n, movesize; | ||
1464 | game_state *ret = dup_game(state); | ||
1465 | |||
1466 | while (*move) { | ||
1467 | c = *move; | ||
1468 | if (c == 'S') { | ||
1469 | /* | ||
1470 | * This is a solve move, so we just set up a stored | ||
1471 | * solution path. | ||
1472 | */ | ||
1473 | if (ret->soln && --ret->soln->refcount <= 0) { | ||
1474 | sfree(ret->soln->moves); | ||
1475 | sfree(ret->soln); | ||
1476 | } | ||
1477 | ret->soln = snew(struct game_solution); | ||
1478 | ret->soln->nmoves = 0; | ||
1479 | ret->soln->moves = NULL; | ||
1480 | ret->soln->refcount = 1; | ||
1481 | ret->soln_index = 0; | ||
1482 | ret->cheated = true; | ||
1483 | |||
1484 | movesize = 0; | ||
1485 | move++; | ||
1486 | while (1) { | ||
1487 | if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2) { | ||
1488 | free_game(ret); | ||
1489 | return NULL; | ||
1490 | } | ||
1491 | |||
1492 | /* | ||
1493 | * Special case: if the first move in the solution | ||
1494 | * involves the piece for which we already have a | ||
1495 | * partial stored move, adjust the source point to | ||
1496 | * the original starting point of that piece. | ||
1497 | */ | ||
1498 | if (ret->soln->nmoves == 0 && a1 == ret->lastmoved) | ||
1499 | a1 = ret->lastmoved_pos; | ||
1500 | |||
1501 | if (ret->soln->nmoves >= movesize) { | ||
1502 | movesize = (ret->soln->nmoves + 48) * 4 / 3; | ||
1503 | ret->soln->moves = sresize(ret->soln->moves, | ||
1504 | 2*movesize, int); | ||
1505 | } | ||
1506 | |||
1507 | ret->soln->moves[2*ret->soln->nmoves] = a1; | ||
1508 | ret->soln->moves[2*ret->soln->nmoves+1] = a2; | ||
1509 | ret->soln->nmoves++; | ||
1510 | move += n; | ||
1511 | if (*move != ',') | ||
1512 | break; | ||
1513 | move++; /* eat comma */ | ||
1514 | } | ||
1515 | } else if (c == 'M') { | ||
1516 | move++; | ||
1517 | if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2 || | ||
1518 | !move_piece(w, h, state->board, ret->board, | ||
1519 | state->imm->forcefield, a1, a2)) { | ||
1520 | free_game(ret); | ||
1521 | return NULL; | ||
1522 | } | ||
1523 | if (a1 == ret->lastmoved) { | ||
1524 | /* | ||
1525 | * If the player has moved the same piece as they | ||
1526 | * moved last time, don't increment the move | ||
1527 | * count. In fact, if they've put the piece back | ||
1528 | * where it started from, _decrement_ the move | ||
1529 | * count. | ||
1530 | */ | ||
1531 | if (a2 == ret->lastmoved_pos) { | ||
1532 | ret->movecount--; /* reverted last move */ | ||
1533 | ret->lastmoved = ret->lastmoved_pos = -1; | ||
1534 | } else { | ||
1535 | ret->lastmoved = a2; | ||
1536 | /* don't change lastmoved_pos */ | ||
1537 | } | ||
1538 | } else { | ||
1539 | ret->lastmoved = a2; | ||
1540 | ret->lastmoved_pos = a1; | ||
1541 | ret->movecount++; | ||
1542 | } | ||
1543 | |||
1544 | /* | ||
1545 | * If we have a stored solution path, see if we've | ||
1546 | * strayed from it or successfully made the next move | ||
1547 | * along it. | ||
1548 | */ | ||
1549 | if (ret->soln && ret->lastmoved_pos >= 0) { | ||
1550 | if (ret->lastmoved_pos != | ||
1551 | ret->soln->moves[ret->soln_index*2]) { | ||
1552 | /* strayed from the path */ | ||
1553 | ret->soln->refcount--; | ||
1554 | assert(ret->soln->refcount > 0); | ||
1555 | /* `state' at least still exists */ | ||
1556 | ret->soln = NULL; | ||
1557 | ret->soln_index = -1; | ||
1558 | } else if (ret->lastmoved == | ||
1559 | ret->soln->moves[ret->soln_index*2+1]) { | ||
1560 | /* advanced along the path */ | ||
1561 | ret->soln_index++; | ||
1562 | if (ret->soln_index >= ret->soln->nmoves) { | ||
1563 | /* finished the path! */ | ||
1564 | ret->soln->refcount--; | ||
1565 | assert(ret->soln->refcount > 0); | ||
1566 | /* `state' at least still exists */ | ||
1567 | ret->soln = NULL; | ||
1568 | ret->soln_index = -1; | ||
1569 | } | ||
1570 | } | ||
1571 | } | ||
1572 | |||
1573 | if (ret->board[a2] == MAINANCHOR && | ||
1574 | a2 == ret->ty * w + ret->tx && ret->completed < 0) | ||
1575 | ret->completed = ret->movecount; | ||
1576 | move += n; | ||
1577 | } else { | ||
1578 | free_game(ret); | ||
1579 | return NULL; | ||
1580 | } | ||
1581 | if (*move == ';') | ||
1582 | move++; | ||
1583 | else if (*move) { | ||
1584 | free_game(ret); | ||
1585 | return NULL; | ||
1586 | } | ||
1587 | } | ||
1588 | |||
1589 | return ret; | ||
1590 | } | ||
1591 | |||
1592 | /* ---------------------------------------------------------------------- | ||
1593 | * Drawing routines. | ||
1594 | */ | ||
1595 | |||
1596 | static void game_compute_size(const game_params *params, int tilesize, | ||
1597 | const game_ui *ui, int *x, int *y) | ||
1598 | { | ||
1599 | /* fool the macros */ | ||
1600 | struct dummy { int tilesize; } dummy, *ds = &dummy; | ||
1601 | dummy.tilesize = tilesize; | ||
1602 | |||
1603 | *x = params->w * TILESIZE + 2*BORDER; | ||
1604 | *y = params->h * TILESIZE + 2*BORDER; | ||
1605 | } | ||
1606 | |||
1607 | static void game_set_size(drawing *dr, game_drawstate *ds, | ||
1608 | const game_params *params, int tilesize) | ||
1609 | { | ||
1610 | ds->tilesize = tilesize; | ||
1611 | } | ||
1612 | |||
1613 | static void raise_colour(float *target, float *src, float *limit) | ||
1614 | { | ||
1615 | int i; | ||
1616 | for (i = 0; i < 3; i++) | ||
1617 | target[i] = (2*src[i] + limit[i]) / 3; | ||
1618 | } | ||
1619 | |||
1620 | static float *game_colours(frontend *fe, int *ncolours) | ||
1621 | { | ||
1622 | float *ret = snewn(3 * NCOLOURS, float); | ||
1623 | |||
1624 | game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); | ||
1625 | |||
1626 | /* | ||
1627 | * When dragging a tile, we light it up a bit. | ||
1628 | */ | ||
1629 | raise_colour(ret+3*COL_DRAGGING, | ||
1630 | ret+3*COL_BACKGROUND, ret+3*COL_HIGHLIGHT); | ||
1631 | raise_colour(ret+3*COL_DRAGGING_HIGHLIGHT, | ||
1632 | ret+3*COL_HIGHLIGHT, ret+3*COL_HIGHLIGHT); | ||
1633 | raise_colour(ret+3*COL_DRAGGING_LOWLIGHT, | ||
1634 | ret+3*COL_LOWLIGHT, ret+3*COL_HIGHLIGHT); | ||
1635 | |||
1636 | /* | ||
1637 | * The main tile is tinted blue. | ||
1638 | */ | ||
1639 | ret[COL_MAIN * 3 + 0] = ret[COL_BACKGROUND * 3 + 0]; | ||
1640 | ret[COL_MAIN * 3 + 1] = ret[COL_BACKGROUND * 3 + 1]; | ||
1641 | ret[COL_MAIN * 3 + 2] = ret[COL_HIGHLIGHT * 3 + 2]; | ||
1642 | game_mkhighlight_specific(fe, ret, COL_MAIN, | ||
1643 | COL_MAIN_HIGHLIGHT, COL_MAIN_LOWLIGHT); | ||
1644 | |||
1645 | /* | ||
1646 | * And we light that up a bit too when dragging. | ||
1647 | */ | ||
1648 | raise_colour(ret+3*COL_MAIN_DRAGGING, | ||
1649 | ret+3*COL_MAIN, ret+3*COL_MAIN_HIGHLIGHT); | ||
1650 | raise_colour(ret+3*COL_MAIN_DRAGGING_HIGHLIGHT, | ||
1651 | ret+3*COL_MAIN_HIGHLIGHT, ret+3*COL_MAIN_HIGHLIGHT); | ||
1652 | raise_colour(ret+3*COL_MAIN_DRAGGING_LOWLIGHT, | ||
1653 | ret+3*COL_MAIN_LOWLIGHT, ret+3*COL_MAIN_HIGHLIGHT); | ||
1654 | |||
1655 | /* | ||
1656 | * The target area on the floor is tinted green. | ||
1657 | */ | ||
1658 | ret[COL_TARGET * 3 + 0] = ret[COL_BACKGROUND * 3 + 0]; | ||
1659 | ret[COL_TARGET * 3 + 1] = ret[COL_HIGHLIGHT * 3 + 1]; | ||
1660 | ret[COL_TARGET * 3 + 2] = ret[COL_BACKGROUND * 3 + 2]; | ||
1661 | game_mkhighlight_specific(fe, ret, COL_TARGET, | ||
1662 | COL_TARGET_HIGHLIGHT, COL_TARGET_LOWLIGHT); | ||
1663 | |||
1664 | *ncolours = NCOLOURS; | ||
1665 | return ret; | ||
1666 | } | ||
1667 | |||
1668 | static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) | ||
1669 | { | ||
1670 | int w = state->w, h = state->h, wh = w*h; | ||
1671 | struct game_drawstate *ds = snew(struct game_drawstate); | ||
1672 | int i; | ||
1673 | |||
1674 | ds->tilesize = 0; | ||
1675 | ds->w = w; | ||
1676 | ds->h = h; | ||
1677 | ds->grid = snewn(wh, unsigned long); | ||
1678 | for (i = 0; i < wh; i++) | ||
1679 | ds->grid[i] = ~(unsigned long)0; | ||
1680 | |||
1681 | return ds; | ||
1682 | } | ||
1683 | |||
1684 | static void game_free_drawstate(drawing *dr, game_drawstate *ds) | ||
1685 | { | ||
1686 | sfree(ds->grid); | ||
1687 | sfree(ds); | ||
1688 | } | ||
1689 | |||
1690 | #define BG_NORMAL 0x00000001UL | ||
1691 | #define BG_TARGET 0x00000002UL | ||
1692 | #define BG_FORCEFIELD 0x00000004UL | ||
1693 | #define FLASH_LOW 0x00000008UL | ||
1694 | #define FLASH_HIGH 0x00000010UL | ||
1695 | #define FG_WALL 0x00000020UL | ||
1696 | #define FG_MAIN 0x00000040UL | ||
1697 | #define FG_NORMAL 0x00000080UL | ||
1698 | #define FG_DRAGGING 0x00000100UL | ||
1699 | #define FG_SHADOW 0x00000200UL | ||
1700 | #define FG_SOLVEPIECE 0x00000400UL | ||
1701 | #define FG_MAINPIECESH 11 | ||
1702 | #define FG_SHADOWSH 19 | ||
1703 | |||
1704 | #define PIECE_LBORDER 0x00000001UL | ||
1705 | #define PIECE_TBORDER 0x00000002UL | ||
1706 | #define PIECE_RBORDER 0x00000004UL | ||
1707 | #define PIECE_BBORDER 0x00000008UL | ||
1708 | #define PIECE_TLCORNER 0x00000010UL | ||
1709 | #define PIECE_TRCORNER 0x00000020UL | ||
1710 | #define PIECE_BLCORNER 0x00000040UL | ||
1711 | #define PIECE_BRCORNER 0x00000080UL | ||
1712 | #define PIECE_MASK 0x000000FFUL | ||
1713 | |||
1714 | /* | ||
1715 | * Utility function. | ||
1716 | */ | ||
1717 | #define TYPE_MASK 0xF000 | ||
1718 | #define COL_MASK 0x0FFF | ||
1719 | #define TYPE_RECT 0x0000 | ||
1720 | #define TYPE_TLCIRC 0x4000 | ||
1721 | #define TYPE_TRCIRC 0x5000 | ||
1722 | #define TYPE_BLCIRC 0x6000 | ||
1723 | #define TYPE_BRCIRC 0x7000 | ||
1724 | static void maybe_rect(drawing *dr, int x, int y, int w, int h, | ||
1725 | int coltype, int col2) | ||
1726 | { | ||
1727 | int colour = coltype & COL_MASK, type = coltype & TYPE_MASK; | ||
1728 | |||
1729 | if (colour > NCOLOURS) | ||
1730 | return; | ||
1731 | if (type == TYPE_RECT) { | ||
1732 | draw_rect(dr, x, y, w, h, colour); | ||
1733 | } else { | ||
1734 | int cx, cy, r; | ||
1735 | |||
1736 | clip(dr, x, y, w, h); | ||
1737 | |||
1738 | cx = x; | ||
1739 | cy = y; | ||
1740 | r = w-1; | ||
1741 | if (type & 0x1000) | ||
1742 | cx += r; | ||
1743 | if (type & 0x2000) | ||
1744 | cy += r; | ||
1745 | |||
1746 | if (col2 == -1 || col2 == coltype) { | ||
1747 | assert(w == h); | ||
1748 | draw_circle(dr, cx, cy, r, colour, colour); | ||
1749 | } else { | ||
1750 | /* | ||
1751 | * We aim to draw a quadrant of a circle in two | ||
1752 | * different colours. We do this using Bresenham's | ||
1753 | * algorithm directly, because the Puzzles drawing API | ||
1754 | * doesn't have a draw-sector primitive. | ||
1755 | */ | ||
1756 | int bx, by, bd, bd2; | ||
1757 | int xm = (type & 0x1000 ? -1 : +1); | ||
1758 | int ym = (type & 0x2000 ? -1 : +1); | ||
1759 | |||
1760 | by = r; | ||
1761 | bx = 0; | ||
1762 | bd = 0; | ||
1763 | while (by >= bx) { | ||
1764 | /* | ||
1765 | * Plot the point. | ||
1766 | */ | ||
1767 | { | ||
1768 | int x1 = cx+xm*bx, y1 = cy+ym*bx; | ||
1769 | int x2, y2; | ||
1770 | |||
1771 | x2 = cx+xm*by; y2 = y1; | ||
1772 | draw_rect(dr, min(x1,x2), min(y1,y2), | ||
1773 | abs(x1-x2)+1, abs(y1-y2)+1, colour); | ||
1774 | x2 = x1; y2 = cy+ym*by; | ||
1775 | draw_rect(dr, min(x1,x2), min(y1,y2), | ||
1776 | abs(x1-x2)+1, abs(y1-y2)+1, col2); | ||
1777 | } | ||
1778 | |||
1779 | bd += 2*bx + 1; | ||
1780 | bd2 = bd - (2*by - 1); | ||
1781 | if (abs(bd2) < abs(bd)) { | ||
1782 | bd = bd2; | ||
1783 | by--; | ||
1784 | } | ||
1785 | bx++; | ||
1786 | } | ||
1787 | } | ||
1788 | |||
1789 | unclip(dr); | ||
1790 | } | ||
1791 | } | ||
1792 | |||
1793 | static void draw_wallpart(drawing *dr, game_drawstate *ds, | ||
1794 | int tx, int ty, unsigned long val, | ||
1795 | int cl, int cc, int ch) | ||
1796 | { | ||
1797 | int coords[6]; | ||
1798 | |||
1799 | draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc); | ||
1800 | if (val & PIECE_LBORDER) | ||
1801 | draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, TILESIZE, | ||
1802 | ch); | ||
1803 | if (val & PIECE_RBORDER) | ||
1804 | draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, | ||
1805 | HIGHLIGHT_WIDTH, TILESIZE, cl); | ||
1806 | if (val & PIECE_TBORDER) | ||
1807 | draw_rect(dr, tx, ty, TILESIZE, HIGHLIGHT_WIDTH, ch); | ||
1808 | if (val & PIECE_BBORDER) | ||
1809 | draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, | ||
1810 | TILESIZE, HIGHLIGHT_WIDTH, cl); | ||
1811 | if (!((PIECE_BBORDER | PIECE_LBORDER) &~ val)) { | ||
1812 | draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, | ||
1813 | HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl); | ||
1814 | clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, | ||
1815 | HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH); | ||
1816 | coords[0] = tx - 1; | ||
1817 | coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1; | ||
1818 | coords[2] = tx + HIGHLIGHT_WIDTH; | ||
1819 | coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1; | ||
1820 | coords[4] = tx - 1; | ||
1821 | coords[5] = ty + TILESIZE; | ||
1822 | draw_polygon(dr, coords, 3, ch, ch); | ||
1823 | unclip(dr); | ||
1824 | } else if (val & PIECE_BLCORNER) { | ||
1825 | draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, | ||
1826 | HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch); | ||
1827 | clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, | ||
1828 | HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH); | ||
1829 | coords[0] = tx - 1; | ||
1830 | coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1; | ||
1831 | coords[2] = tx + HIGHLIGHT_WIDTH; | ||
1832 | coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1; | ||
1833 | coords[4] = tx - 1; | ||
1834 | coords[5] = ty + TILESIZE; | ||
1835 | draw_polygon(dr, coords, 3, cl, cl); | ||
1836 | unclip(dr); | ||
1837 | } | ||
1838 | if (!((PIECE_TBORDER | PIECE_RBORDER) &~ val)) { | ||
1839 | draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, | ||
1840 | HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl); | ||
1841 | clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, | ||
1842 | HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH); | ||
1843 | coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1; | ||
1844 | coords[1] = ty - 1; | ||
1845 | coords[2] = tx + TILESIZE; | ||
1846 | coords[3] = ty - 1; | ||
1847 | coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1; | ||
1848 | coords[5] = ty + HIGHLIGHT_WIDTH; | ||
1849 | draw_polygon(dr, coords, 3, ch, ch); | ||
1850 | unclip(dr); | ||
1851 | } else if (val & PIECE_TRCORNER) { | ||
1852 | draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, | ||
1853 | HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch); | ||
1854 | clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, | ||
1855 | HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH); | ||
1856 | coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1; | ||
1857 | coords[1] = ty - 1; | ||
1858 | coords[2] = tx + TILESIZE; | ||
1859 | coords[3] = ty - 1; | ||
1860 | coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1; | ||
1861 | coords[5] = ty + HIGHLIGHT_WIDTH; | ||
1862 | draw_polygon(dr, coords, 3, cl, cl); | ||
1863 | unclip(dr); | ||
1864 | } | ||
1865 | if (val & PIECE_TLCORNER) | ||
1866 | draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch); | ||
1867 | if (val & PIECE_BRCORNER) | ||
1868 | draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, | ||
1869 | ty+TILESIZE-HIGHLIGHT_WIDTH, | ||
1870 | HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl); | ||
1871 | } | ||
1872 | |||
1873 | static void draw_piecepart(drawing *dr, game_drawstate *ds, | ||
1874 | int tx, int ty, unsigned long val, | ||
1875 | int cl, int cc, int ch) | ||
1876 | { | ||
1877 | int x[6], y[6]; | ||
1878 | |||
1879 | /* | ||
1880 | * Drawing the blocks is hellishly fiddly. The blocks don't | ||
1881 | * stretch to the full size of the tile; there's a border | ||
1882 | * around them of size BORDER_WIDTH. Then they have bevelled | ||
1883 | * borders of size HIGHLIGHT_WIDTH, and also rounded corners. | ||
1884 | * | ||
1885 | * I tried for some time to find a clean and clever way to | ||
1886 | * figure out what needed drawing from the corner and border | ||
1887 | * flags, but in the end the cleanest way I could find was the | ||
1888 | * following. We divide the grid square into 25 parts by | ||
1889 | * ruling four horizontal and four vertical lines across it; | ||
1890 | * those lines are at BORDER_WIDTH and BORDER_WIDTH + | ||
1891 | * HIGHLIGHT_WIDTH from the top, from the bottom, from the | ||
1892 | * left and from the right. Then we carefully consider each of | ||
1893 | * the resulting 25 sections of square, and decide separately | ||
1894 | * what needs to go in it based on the flags. In complicated | ||
1895 | * cases there can be up to five possibilities affecting any | ||
1896 | * given section (no corner or border flags, just the corner | ||
1897 | * flag, one border flag, the other border flag, both border | ||
1898 | * flags). So there's a lot of very fiddly logic here and all | ||
1899 | * I could really think to do was give it my best shot and | ||
1900 | * then test it and correct all the typos. Not fun to write, | ||
1901 | * and I'm sure it isn't fun to read either, but it seems to | ||
1902 | * work. | ||
1903 | */ | ||
1904 | |||
1905 | x[0] = tx; | ||
1906 | x[1] = x[0] + BORDER_WIDTH; | ||
1907 | x[2] = x[1] + HIGHLIGHT_WIDTH; | ||
1908 | x[5] = tx + TILESIZE; | ||
1909 | x[4] = x[5] - BORDER_WIDTH; | ||
1910 | x[3] = x[4] - HIGHLIGHT_WIDTH; | ||
1911 | |||
1912 | y[0] = ty; | ||
1913 | y[1] = y[0] + BORDER_WIDTH; | ||
1914 | y[2] = y[1] + HIGHLIGHT_WIDTH; | ||
1915 | y[5] = ty + TILESIZE; | ||
1916 | y[4] = y[5] - BORDER_WIDTH; | ||
1917 | y[3] = y[4] - HIGHLIGHT_WIDTH; | ||
1918 | |||
1919 | #define RECT(p,q) x[p], y[q], x[(p)+1]-x[p], y[(q)+1]-y[q] | ||
1920 | |||
1921 | maybe_rect(dr, RECT(0,0), | ||
1922 | (val & (PIECE_TLCORNER | PIECE_TBORDER | | ||
1923 | PIECE_LBORDER)) ? -1 : cc, -1); | ||
1924 | maybe_rect(dr, RECT(1,0), | ||
1925 | (val & PIECE_TLCORNER) ? ch : (val & PIECE_TBORDER) ? -1 : | ||
1926 | (val & PIECE_LBORDER) ? ch : cc, -1); | ||
1927 | maybe_rect(dr, RECT(2,0), | ||
1928 | (val & PIECE_TBORDER) ? -1 : cc, -1); | ||
1929 | maybe_rect(dr, RECT(3,0), | ||
1930 | (val & PIECE_TRCORNER) ? cl : (val & PIECE_TBORDER) ? -1 : | ||
1931 | (val & PIECE_RBORDER) ? cl : cc, -1); | ||
1932 | maybe_rect(dr, RECT(4,0), | ||
1933 | (val & (PIECE_TRCORNER | PIECE_TBORDER | | ||
1934 | PIECE_RBORDER)) ? -1 : cc, -1); | ||
1935 | maybe_rect(dr, RECT(0,1), | ||
1936 | (val & PIECE_TLCORNER) ? ch : (val & PIECE_LBORDER) ? -1 : | ||
1937 | (val & PIECE_TBORDER) ? ch : cc, -1); | ||
1938 | maybe_rect(dr, RECT(1,1), | ||
1939 | (val & PIECE_TLCORNER) ? cc : -1, -1); | ||
1940 | maybe_rect(dr, RECT(1,1), | ||
1941 | (val & PIECE_TLCORNER) ? ch | TYPE_TLCIRC : | ||
1942 | !((PIECE_TBORDER | PIECE_LBORDER) &~ val) ? ch | TYPE_BRCIRC : | ||
1943 | (val & (PIECE_TBORDER | PIECE_LBORDER)) ? ch : cc, -1); | ||
1944 | maybe_rect(dr, RECT(2,1), | ||
1945 | (val & PIECE_TBORDER) ? ch : cc, -1); | ||
1946 | maybe_rect(dr, RECT(3,1), | ||
1947 | (val & PIECE_TRCORNER) ? cc : -1, -1); | ||
1948 | maybe_rect(dr, RECT(3,1), | ||
1949 | (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_TBORDER ? ch : | ||
1950 | (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_RBORDER ? cl : | ||
1951 | !((PIECE_TBORDER|PIECE_RBORDER) &~ val) ? cl | TYPE_BLCIRC : | ||
1952 | (val & PIECE_TRCORNER) ? cl | TYPE_TRCIRC : | ||
1953 | cc, ch); | ||
1954 | maybe_rect(dr, RECT(4,1), | ||
1955 | (val & PIECE_TRCORNER) ? ch : (val & PIECE_RBORDER) ? -1 : | ||
1956 | (val & PIECE_TBORDER) ? ch : cc, -1); | ||
1957 | maybe_rect(dr, RECT(0,2), | ||
1958 | (val & PIECE_LBORDER) ? -1 : cc, -1); | ||
1959 | maybe_rect(dr, RECT(1,2), | ||
1960 | (val & PIECE_LBORDER) ? ch : cc, -1); | ||
1961 | maybe_rect(dr, RECT(2,2), | ||
1962 | cc, -1); | ||
1963 | maybe_rect(dr, RECT(3,2), | ||
1964 | (val & PIECE_RBORDER) ? cl : cc, -1); | ||
1965 | maybe_rect(dr, RECT(4,2), | ||
1966 | (val & PIECE_RBORDER) ? -1 : cc, -1); | ||
1967 | maybe_rect(dr, RECT(0,3), | ||
1968 | (val & PIECE_BLCORNER) ? cl : (val & PIECE_LBORDER) ? -1 : | ||
1969 | (val & PIECE_BBORDER) ? cl : cc, -1); | ||
1970 | maybe_rect(dr, RECT(1,3), | ||
1971 | (val & PIECE_BLCORNER) ? cc : -1, -1); | ||
1972 | maybe_rect(dr, RECT(1,3), | ||
1973 | (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_BBORDER ? cl : | ||
1974 | (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_LBORDER ? ch : | ||
1975 | !((PIECE_BBORDER|PIECE_LBORDER) &~ val) ? ch | TYPE_TRCIRC : | ||
1976 | (val & PIECE_BLCORNER) ? ch | TYPE_BLCIRC : | ||
1977 | cc, cl); | ||
1978 | maybe_rect(dr, RECT(2,3), | ||
1979 | (val & PIECE_BBORDER) ? cl : cc, -1); | ||
1980 | maybe_rect(dr, RECT(3,3), | ||
1981 | (val & PIECE_BRCORNER) ? cc : -1, -1); | ||
1982 | maybe_rect(dr, RECT(3,3), | ||
1983 | (val & PIECE_BRCORNER) ? cl | TYPE_BRCIRC : | ||
1984 | !((PIECE_BBORDER | PIECE_RBORDER) &~ val) ? cl | TYPE_TLCIRC : | ||
1985 | (val & (PIECE_BBORDER | PIECE_RBORDER)) ? cl : cc, -1); | ||
1986 | maybe_rect(dr, RECT(4,3), | ||
1987 | (val & PIECE_BRCORNER) ? cl : (val & PIECE_RBORDER) ? -1 : | ||
1988 | (val & PIECE_BBORDER) ? cl : cc, -1); | ||
1989 | maybe_rect(dr, RECT(0,4), | ||
1990 | (val & (PIECE_BLCORNER | PIECE_BBORDER | | ||
1991 | PIECE_LBORDER)) ? -1 : cc, -1); | ||
1992 | maybe_rect(dr, RECT(1,4), | ||
1993 | (val & PIECE_BLCORNER) ? ch : (val & PIECE_BBORDER) ? -1 : | ||
1994 | (val & PIECE_LBORDER) ? ch : cc, -1); | ||
1995 | maybe_rect(dr, RECT(2,4), | ||
1996 | (val & PIECE_BBORDER) ? -1 : cc, -1); | ||
1997 | maybe_rect(dr, RECT(3,4), | ||
1998 | (val & PIECE_BRCORNER) ? cl : (val & PIECE_BBORDER) ? -1 : | ||
1999 | (val & PIECE_RBORDER) ? cl : cc, -1); | ||
2000 | maybe_rect(dr, RECT(4,4), | ||
2001 | (val & (PIECE_BRCORNER | PIECE_BBORDER | | ||
2002 | PIECE_RBORDER)) ? -1 : cc, -1); | ||
2003 | |||
2004 | #undef RECT | ||
2005 | } | ||
2006 | |||
2007 | static void draw_tile(drawing *dr, game_drawstate *ds, | ||
2008 | int x, int y, unsigned long val) | ||
2009 | { | ||
2010 | int tx = COORD(x), ty = COORD(y); | ||
2011 | int cc, ch, cl; | ||
2012 | |||
2013 | /* | ||
2014 | * Draw the tile background. | ||
2015 | */ | ||
2016 | if (val & BG_TARGET) | ||
2017 | cc = COL_TARGET; | ||
2018 | else | ||
2019 | cc = COL_BACKGROUND; | ||
2020 | ch = cc+1; | ||
2021 | cl = cc+2; | ||
2022 | if (val & FLASH_LOW) | ||
2023 | cc = cl; | ||
2024 | else if (val & FLASH_HIGH) | ||
2025 | cc = ch; | ||
2026 | |||
2027 | draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc); | ||
2028 | if (val & BG_FORCEFIELD) { | ||
2029 | /* | ||
2030 | * Cattle-grid effect to indicate that nothing but the | ||
2031 | * main block can slide over this square. | ||
2032 | */ | ||
2033 | int n = 3 * (TILESIZE / (3*HIGHLIGHT_WIDTH)); | ||
2034 | int i; | ||
2035 | |||
2036 | for (i = 1; i < n; i += 3) { | ||
2037 | draw_rect(dr, tx,ty+(TILESIZE*i/n), TILESIZE,HIGHLIGHT_WIDTH, cl); | ||
2038 | draw_rect(dr, tx+(TILESIZE*i/n),ty, HIGHLIGHT_WIDTH,TILESIZE, cl); | ||
2039 | } | ||
2040 | } | ||
2041 | |||
2042 | /* | ||
2043 | * Draw the tile midground: a shadow of a block, for | ||
2044 | * displaying partial solutions. | ||
2045 | */ | ||
2046 | if (val & FG_SHADOW) { | ||
2047 | draw_piecepart(dr, ds, tx, ty, (val >> FG_SHADOWSH) & PIECE_MASK, | ||
2048 | cl, cl, cl); | ||
2049 | } | ||
2050 | |||
2051 | /* | ||
2052 | * Draw the tile foreground, i.e. some section of a block or | ||
2053 | * wall. | ||
2054 | */ | ||
2055 | if (val & FG_WALL) { | ||
2056 | cc = COL_BACKGROUND; | ||
2057 | ch = cc+1; | ||
2058 | cl = cc+2; | ||
2059 | if (val & FLASH_LOW) | ||
2060 | cc = cl; | ||
2061 | else if (val & FLASH_HIGH) | ||
2062 | cc = ch; | ||
2063 | |||
2064 | draw_wallpart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK, | ||
2065 | cl, cc, ch); | ||
2066 | } else if (val & (FG_MAIN | FG_NORMAL)) { | ||
2067 | if (val & FG_DRAGGING) | ||
2068 | cc = (val & FG_MAIN ? COL_MAIN_DRAGGING : COL_DRAGGING); | ||
2069 | else | ||
2070 | cc = (val & FG_MAIN ? COL_MAIN : COL_BACKGROUND); | ||
2071 | ch = cc+1; | ||
2072 | cl = cc+2; | ||
2073 | |||
2074 | if (val & FLASH_LOW) | ||
2075 | cc = cl; | ||
2076 | else if (val & (FLASH_HIGH | FG_SOLVEPIECE)) | ||
2077 | cc = ch; | ||
2078 | |||
2079 | draw_piecepart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK, | ||
2080 | cl, cc, ch); | ||
2081 | } | ||
2082 | |||
2083 | draw_update(dr, tx, ty, TILESIZE, TILESIZE); | ||
2084 | } | ||
2085 | |||
2086 | static unsigned long find_piecepart(int w, int h, DSF *dsf, int x, int y) | ||
2087 | { | ||
2088 | int i = y*w+x; | ||
2089 | int canon = dsf_canonify(dsf, i); | ||
2090 | unsigned long val = 0; | ||
2091 | |||
2092 | if (x == 0 || canon != dsf_canonify(dsf, i-1)) | ||
2093 | val |= PIECE_LBORDER; | ||
2094 | if (y== 0 || canon != dsf_canonify(dsf, i-w)) | ||
2095 | val |= PIECE_TBORDER; | ||
2096 | if (x == w-1 || canon != dsf_canonify(dsf, i+1)) | ||
2097 | val |= PIECE_RBORDER; | ||
2098 | if (y == h-1 || canon != dsf_canonify(dsf, i+w)) | ||
2099 | val |= PIECE_BBORDER; | ||
2100 | if (!(val & (PIECE_TBORDER | PIECE_LBORDER)) && | ||
2101 | canon != dsf_canonify(dsf, i-1-w)) | ||
2102 | val |= PIECE_TLCORNER; | ||
2103 | if (!(val & (PIECE_TBORDER | PIECE_RBORDER)) && | ||
2104 | canon != dsf_canonify(dsf, i+1-w)) | ||
2105 | val |= PIECE_TRCORNER; | ||
2106 | if (!(val & (PIECE_BBORDER | PIECE_LBORDER)) && | ||
2107 | canon != dsf_canonify(dsf, i-1+w)) | ||
2108 | val |= PIECE_BLCORNER; | ||
2109 | if (!(val & (PIECE_BBORDER | PIECE_RBORDER)) && | ||
2110 | canon != dsf_canonify(dsf, i+1+w)) | ||
2111 | val |= PIECE_BRCORNER; | ||
2112 | return val; | ||
2113 | } | ||
2114 | |||
2115 | static void game_redraw(drawing *dr, game_drawstate *ds, | ||
2116 | const game_state *oldstate, const game_state *state, | ||
2117 | int dir, const game_ui *ui, | ||
2118 | float animtime, float flashtime) | ||
2119 | { | ||
2120 | int w = state->w, h = state->h, wh = w*h; | ||
2121 | unsigned char *board; | ||
2122 | DSF *dsf; | ||
2123 | int x, y, mainanchor, mainpos, dragpos, solvepos, solvesrc, solvedst; | ||
2124 | |||
2125 | /* | ||
2126 | * Construct the board we'll be displaying (which may be | ||
2127 | * different from the one in state if ui describes a drag in | ||
2128 | * progress). | ||
2129 | */ | ||
2130 | board = snewn(wh, unsigned char); | ||
2131 | memcpy(board, state->board, wh); | ||
2132 | if (ui->dragging) { | ||
2133 | bool mpret = move_piece(w, h, state->board, board, | ||
2134 | state->imm->forcefield, | ||
2135 | ui->drag_anchor, ui->drag_currpos); | ||
2136 | assert(mpret); | ||
2137 | } | ||
2138 | |||
2139 | if (state->soln) { | ||
2140 | solvesrc = state->soln->moves[state->soln_index*2]; | ||
2141 | solvedst = state->soln->moves[state->soln_index*2+1]; | ||
2142 | if (solvesrc == state->lastmoved_pos) | ||
2143 | solvesrc = state->lastmoved; | ||
2144 | if (solvesrc == ui->drag_anchor) | ||
2145 | solvesrc = ui->drag_currpos; | ||
2146 | } else | ||
2147 | solvesrc = solvedst = -1; | ||
2148 | |||
2149 | /* | ||
2150 | * Build a dsf out of that board, so we can conveniently tell | ||
2151 | * which edges are connected and which aren't. | ||
2152 | */ | ||
2153 | dsf = dsf_new(wh); | ||
2154 | mainanchor = -1; | ||
2155 | for (y = 0; y < h; y++) | ||
2156 | for (x = 0; x < w; x++) { | ||
2157 | int i = y*w+x; | ||
2158 | |||
2159 | if (ISDIST(board[i])) | ||
2160 | dsf_merge(dsf, i, i - board[i]); | ||
2161 | if (board[i] == MAINANCHOR) | ||
2162 | mainanchor = i; | ||
2163 | if (board[i] == WALL) { | ||
2164 | if (x > 0 && board[i-1] == WALL) | ||
2165 | dsf_merge(dsf, i, i-1); | ||
2166 | if (y > 0 && board[i-w] == WALL) | ||
2167 | dsf_merge(dsf, i, i-w); | ||
2168 | } | ||
2169 | } | ||
2170 | assert(mainanchor >= 0); | ||
2171 | mainpos = dsf_canonify(dsf, mainanchor); | ||
2172 | dragpos = ui->drag_currpos > 0 ? dsf_canonify(dsf, ui->drag_currpos) : -1; | ||
2173 | solvepos = solvesrc >= 0 ? dsf_canonify(dsf, solvesrc) : -1; | ||
2174 | |||
2175 | /* | ||
2176 | * Now we can construct the data about what we want to draw. | ||
2177 | */ | ||
2178 | for (y = 0; y < h; y++) | ||
2179 | for (x = 0; x < w; x++) { | ||
2180 | int i = y*w+x; | ||
2181 | int j; | ||
2182 | unsigned long val; | ||
2183 | int canon; | ||
2184 | |||
2185 | /* | ||
2186 | * See if this square is part of the target area. | ||
2187 | */ | ||
2188 | j = i + mainanchor - (state->ty * w + state->tx); | ||
2189 | while (j >= 0 && j < wh && ISDIST(board[j])) | ||
2190 | j -= board[j]; | ||
2191 | if (j == mainanchor) | ||
2192 | val = BG_TARGET; | ||
2193 | else | ||
2194 | val = BG_NORMAL; | ||
2195 | |||
2196 | if (state->imm->forcefield[i]) | ||
2197 | val |= BG_FORCEFIELD; | ||
2198 | |||
2199 | if (flashtime > 0) { | ||
2200 | int flashtype = (int)(flashtime / FLASH_INTERVAL) & 1; | ||
2201 | val |= (flashtype ? FLASH_LOW : FLASH_HIGH); | ||
2202 | } | ||
2203 | |||
2204 | if (board[i] != EMPTY) { | ||
2205 | canon = dsf_canonify(dsf, i); | ||
2206 | |||
2207 | if (board[i] == WALL) | ||
2208 | val |= FG_WALL; | ||
2209 | else if (canon == mainpos) | ||
2210 | val |= FG_MAIN; | ||
2211 | else | ||
2212 | val |= FG_NORMAL; | ||
2213 | if (canon == dragpos) | ||
2214 | val |= FG_DRAGGING; | ||
2215 | if (canon == solvepos) | ||
2216 | val |= FG_SOLVEPIECE; | ||
2217 | |||
2218 | /* | ||
2219 | * Now look around to see if other squares | ||
2220 | * belonging to the same block are adjacent to us. | ||
2221 | */ | ||
2222 | val |= find_piecepart(w, h, dsf, x, y) << FG_MAINPIECESH; | ||
2223 | } | ||
2224 | |||
2225 | /* | ||
2226 | * If we're in the middle of showing a solution, | ||
2227 | * display a shadow piece for the target of the | ||
2228 | * current move. | ||
2229 | */ | ||
2230 | if (solvepos >= 0) { | ||
2231 | int si = i - solvedst + solvesrc; | ||
2232 | if (si >= 0 && si < wh && dsf_canonify(dsf, si) == solvepos) { | ||
2233 | val |= find_piecepart(w, h, dsf, | ||
2234 | si % w, si / w) << FG_SHADOWSH; | ||
2235 | val |= FG_SHADOW; | ||
2236 | } | ||
2237 | } | ||
2238 | |||
2239 | if (val != ds->grid[i]) { | ||
2240 | draw_tile(dr, ds, x, y, val); | ||
2241 | ds->grid[i] = val; | ||
2242 | } | ||
2243 | } | ||
2244 | |||
2245 | /* | ||
2246 | * Update the status bar. | ||
2247 | */ | ||
2248 | { | ||
2249 | char statusbuf[256]; | ||
2250 | |||
2251 | sprintf(statusbuf, "%sMoves: %d", | ||
2252 | (state->completed >= 0 ? | ||
2253 | (state->cheated ? "Auto-solved. " : "COMPLETED! ") : | ||
2254 | (state->cheated ? "Auto-solver used. " : "")), | ||
2255 | (state->completed >= 0 ? state->completed : state->movecount)); | ||
2256 | if (state->minmoves >= 0) | ||
2257 | sprintf(statusbuf+strlen(statusbuf), " (min %d)", | ||
2258 | state->minmoves); | ||
2259 | |||
2260 | status_bar(dr, statusbuf); | ||
2261 | } | ||
2262 | |||
2263 | dsf_free(dsf); | ||
2264 | sfree(board); | ||
2265 | } | ||
2266 | |||
2267 | static float game_anim_length(const game_state *oldstate, | ||
2268 | const game_state *newstate, int dir, game_ui *ui) | ||
2269 | { | ||
2270 | return 0.0F; | ||
2271 | } | ||
2272 | |||
2273 | static float game_flash_length(const game_state *oldstate, | ||
2274 | const game_state *newstate, int dir, game_ui *ui) | ||
2275 | { | ||
2276 | if (oldstate->completed < 0 && newstate->completed >= 0) | ||
2277 | return FLASH_TIME; | ||
2278 | |||
2279 | return 0.0F; | ||
2280 | } | ||
2281 | |||
2282 | static void game_get_cursor_location(const game_ui *ui, | ||
2283 | const game_drawstate *ds, | ||
2284 | const game_state *state, | ||
2285 | const game_params *params, | ||
2286 | int *x, int *y, int *w, int *h) | ||
2287 | { | ||
2288 | } | ||
2289 | |||
2290 | static int game_status(const game_state *state) | ||
2291 | { | ||
2292 | return state->completed ? +1 : 0; | ||
2293 | } | ||
2294 | |||
2295 | static bool game_timing_state(const game_state *state, game_ui *ui) | ||
2296 | { | ||
2297 | return true; | ||
2298 | } | ||
2299 | |||
2300 | static void game_print_size(const game_params *params, const game_ui *ui, | ||
2301 | float *x, float *y) | ||
2302 | { | ||
2303 | } | ||
2304 | |||
2305 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, | ||
2306 | int tilesize) | ||
2307 | { | ||
2308 | } | ||
2309 | |||
2310 | #ifdef COMBINED | ||
2311 | #define thegame slide | ||
2312 | #endif | ||
2313 | |||
2314 | const struct game thegame = { | ||
2315 | "Slide", NULL, NULL, | ||
2316 | default_params, | ||
2317 | game_fetch_preset, NULL, | ||
2318 | decode_params, | ||
2319 | encode_params, | ||
2320 | free_params, | ||
2321 | dup_params, | ||
2322 | true, game_configure, custom_params, | ||
2323 | validate_params, | ||
2324 | new_game_desc, | ||
2325 | validate_desc, | ||
2326 | new_game, | ||
2327 | dup_game, | ||
2328 | free_game, | ||
2329 | true, solve_game, | ||
2330 | true, game_can_format_as_text_now, game_text_format, | ||
2331 | NULL, NULL, /* get_prefs, set_prefs */ | ||
2332 | new_ui, | ||
2333 | free_ui, | ||
2334 | NULL, /* encode_ui */ | ||
2335 | NULL, /* decode_ui */ | ||
2336 | NULL, /* game_request_keys */ | ||
2337 | game_changed_state, | ||
2338 | NULL, /* current_key_label */ | ||
2339 | interpret_move, | ||
2340 | execute_move, | ||
2341 | PREFERRED_TILESIZE, game_compute_size, game_set_size, | ||
2342 | game_colours, | ||
2343 | game_new_drawstate, | ||
2344 | game_free_drawstate, | ||
2345 | game_redraw, | ||
2346 | game_anim_length, | ||
2347 | game_flash_length, | ||
2348 | game_get_cursor_location, | ||
2349 | game_status, | ||
2350 | false, false, game_print_size, game_print, | ||
2351 | true, /* wants_statusbar */ | ||
2352 | false, game_timing_state, | ||
2353 | 0, /* flags */ | ||
2354 | }; | ||
2355 | |||
2356 | #ifdef STANDALONE_SOLVER | ||
2357 | |||
2358 | #include <stdarg.h> | ||
2359 | |||
2360 | int main(int argc, char **argv) | ||
2361 | { | ||
2362 | game_params *p; | ||
2363 | game_state *s; | ||
2364 | char *id = NULL, *desc; | ||
2365 | const char *err; | ||
2366 | bool count = false; | ||
2367 | int ret; | ||
2368 | int *moves; | ||
2369 | |||
2370 | while (--argc > 0) { | ||
2371 | char *p = *++argv; | ||
2372 | /* | ||
2373 | if (!strcmp(p, "-v")) { | ||
2374 | verbose = true; | ||
2375 | } else | ||
2376 | */ | ||
2377 | if (!strcmp(p, "-c")) { | ||
2378 | count = true; | ||
2379 | } else if (*p == '-') { | ||
2380 | fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); | ||
2381 | return 1; | ||
2382 | } else { | ||
2383 | id = p; | ||
2384 | } | ||
2385 | } | ||
2386 | |||
2387 | if (!id) { | ||
2388 | fprintf(stderr, "usage: %s [-c | -v] <game_id>\n", argv[0]); | ||
2389 | return 1; | ||
2390 | } | ||
2391 | |||
2392 | desc = strchr(id, ':'); | ||
2393 | if (!desc) { | ||
2394 | fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]); | ||
2395 | return 1; | ||
2396 | } | ||
2397 | *desc++ = '\0'; | ||
2398 | |||
2399 | p = default_params(); | ||
2400 | decode_params(p, id); | ||
2401 | err = validate_desc(p, desc); | ||
2402 | if (err) { | ||
2403 | fprintf(stderr, "%s: %s\n", argv[0], err); | ||
2404 | return 1; | ||
2405 | } | ||
2406 | s = new_game(NULL, p, desc); | ||
2407 | |||
2408 | ret = solve_board(s->w, s->h, s->board, s->imm->forcefield, | ||
2409 | s->tx, s->ty, -1, &moves); | ||
2410 | if (ret < 0) { | ||
2411 | printf("No solution found\n"); | ||
2412 | } else { | ||
2413 | int index = 0; | ||
2414 | if (count) { | ||
2415 | printf("%d moves required\n", ret); | ||
2416 | return 0; | ||
2417 | } | ||
2418 | while (1) { | ||
2419 | bool moveret; | ||
2420 | char *text = board_text_format(s->w, s->h, s->board, | ||
2421 | s->imm->forcefield); | ||
2422 | game_state *s2; | ||
2423 | |||
2424 | printf("position %d:\n%s", index, text); | ||
2425 | |||
2426 | if (index >= ret) | ||
2427 | break; | ||
2428 | |||
2429 | s2 = dup_game(s); | ||
2430 | moveret = move_piece(s->w, s->h, s->board, | ||
2431 | s2->board, s->imm->forcefield, | ||
2432 | moves[index*2], moves[index*2+1]); | ||
2433 | assert(moveret); | ||
2434 | |||
2435 | free_game(s); | ||
2436 | s = s2; | ||
2437 | index++; | ||
2438 | } | ||
2439 | } | ||
2440 | |||
2441 | return 0; | ||
2442 | } | ||
2443 | |||
2444 | #endif | ||
diff --git a/apps/plugins/puzzles/src/unfinished/sokoban.c b/apps/plugins/puzzles/src/unfinished/sokoban.c new file mode 100644 index 0000000000..1f3c688af1 --- /dev/null +++ b/apps/plugins/puzzles/src/unfinished/sokoban.c | |||
@@ -0,0 +1,1476 @@ | |||
1 | /* | ||
2 | * sokoban.c: An implementation of the well-known Sokoban barrel- | ||
3 | * pushing game. Random generation is too simplistic to be | ||
4 | * credible, but the rest of the gameplay works well enough to use | ||
5 | * it with hand-written level descriptions. | ||
6 | */ | ||
7 | |||
8 | /* | ||
9 | * TODO: | ||
10 | * | ||
11 | * - I think it would be better to ditch the `prev' array, and | ||
12 | * instead make the `dist' array strictly monotonic (by having | ||
13 | * each distance be something like I*A+S, where A is the grid | ||
14 | * area, I the number of INITIAL squares trampled on, and S the | ||
15 | * number of harmless spaces moved through). This would permit | ||
16 | * the path-tracing when a pull is actually made to choose | ||
17 | * randomly from all the possible shortest routes, which would | ||
18 | * be superior in terms of eliminating directional bias. | ||
19 | * + So when tracing the path back to the current px,py, we | ||
20 | * look at all four adjacent squares, find the minimum | ||
21 | * distance, check that it's _strictly smaller_ than that of | ||
22 | * the current square, and restrict our choice to precisely | ||
23 | * those squares with that minimum distance. | ||
24 | * + The other place `prev' is currently used is in the check | ||
25 | * for consistency of a pull. We would have to replace the | ||
26 | * check for whether prev[ny*w+nx]==oy*w+ox with a check that | ||
27 | * made sure there was at least one adjacent square with a | ||
28 | * smaller distance which _wasn't_ oy*w+ox. Then when we did | ||
29 | * the path-tracing we'd also have to take this special case | ||
30 | * into account. | ||
31 | * | ||
32 | * - More discriminating choice of pull. (Snigger.) | ||
33 | * + favour putting targets in clumps | ||
34 | * + try to shoot for a reasonably consistent number of barrels | ||
35 | * (adjust willingness to generate a new barrel depending on | ||
36 | * how many are already present) | ||
37 | * + adjust willingness to break new ground depending on how | ||
38 | * much is already broken | ||
39 | * | ||
40 | * - generation time parameters: | ||
41 | * + enable NetHack mode (and find a better place for the hole) | ||
42 | * + decide how many of the remaining Is should be walls | ||
43 | * | ||
44 | * - at the end of generation, randomly position the starting | ||
45 | * player coordinates, probably by (somehow) reusing the same | ||
46 | * bfs currently inside the loop. | ||
47 | * | ||
48 | * - possible backtracking? | ||
49 | * | ||
50 | * - IWBNI we could spot completely unreachable bits of level at | ||
51 | * the outside, and not bother drawing grid lines for them. The | ||
52 | * NH levels currently look a bit weird with grid lines on the | ||
53 | * outside of the boundary. | ||
54 | */ | ||
55 | |||
56 | #include <stdio.h> | ||
57 | #include <stdlib.h> | ||
58 | #include <string.h> | ||
59 | #include <assert.h> | ||
60 | #include <ctype.h> | ||
61 | #ifdef NO_TGMATH_H | ||
62 | # include <math.h> | ||
63 | #else | ||
64 | # include <tgmath.h> | ||
65 | #endif | ||
66 | |||
67 | #include "puzzles.h" | ||
68 | |||
69 | /* | ||
70 | * Various subsets of these constants are used during game | ||
71 | * generation, game play, game IDs and the game_drawstate. | ||
72 | */ | ||
73 | #define INITIAL 'i' /* used only in game generation */ | ||
74 | #define SPACE 's' | ||
75 | #define WALL 'w' | ||
76 | #define PIT 'p' | ||
77 | #define DEEP_PIT 'd' | ||
78 | #define TARGET 't' | ||
79 | #define BARREL 'b' | ||
80 | #define BARRELTARGET 'f' /* target is 'f'illed */ | ||
81 | #define PLAYER 'u' /* yo'u'; used in game IDs */ | ||
82 | #define PLAYERTARGET 'v' /* bad letter: v is to u as t is to s */ | ||
83 | #define INVALID '!' /* used in drawstate to force redraw */ | ||
84 | /* | ||
85 | * We also support the use of any capital letter as a barrel, which | ||
86 | * will be displayed with that letter as a label. (This facilitates | ||
87 | * people distributing annotated game IDs for particular Sokoban | ||
88 | * levels, so they can accompany them with verbal instructions | ||
89 | * about pushing particular barrels in particular ways.) Therefore, | ||
90 | * to find out whether something is a barrel, we need a test | ||
91 | * function which does a bit more than just comparing to BARREL. | ||
92 | * | ||
93 | * When resting on target squares, capital-letter barrels are | ||
94 | * replaced with their control-character value (A -> ^A). | ||
95 | */ | ||
96 | #define IS_PLAYER(c) ( (c)==PLAYER || (c)==PLAYERTARGET ) | ||
97 | #define IS_BARREL(c) ( (c)==BARREL || (c)==BARRELTARGET || \ | ||
98 | ((c)>='A' && (c)<='Z') || ((c)>=1 && (c)<=26) ) | ||
99 | #define IS_ON_TARGET(c) ( (c)==TARGET || (c)==BARRELTARGET || \ | ||
100 | (c)==PLAYERTARGET || ((c)>=1 && (c)<=26) ) | ||
101 | #define TARGETISE(b) ( (b)==BARREL ? BARRELTARGET : (b)-('A'-1) ) | ||
102 | #define DETARGETISE(b) ( (b)==BARRELTARGET ? BARREL : (b)+('A'-1) ) | ||
103 | #define BARREL_LABEL(b) ( (b)>='A'&&(b)<='Z' ? (b) : \ | ||
104 | (b)>=1 && (b)<=26 ? (b)+('A'-1) : 0 ) | ||
105 | |||
106 | #define DX(d) (d == 0 ? -1 : d == 2 ? +1 : 0) | ||
107 | #define DY(d) (d == 1 ? -1 : d == 3 ? +1 : 0) | ||
108 | |||
109 | #define FLASH_LENGTH 0.3F | ||
110 | |||
111 | enum { | ||
112 | COL_BACKGROUND, | ||
113 | COL_TARGET, | ||
114 | COL_PIT, | ||
115 | COL_DEEP_PIT, | ||
116 | COL_BARREL, | ||
117 | COL_PLAYER, | ||
118 | COL_TEXT, | ||
119 | COL_GRID, | ||
120 | COL_OUTLINE, | ||
121 | COL_HIGHLIGHT, | ||
122 | COL_LOWLIGHT, | ||
123 | COL_WALL, | ||
124 | NCOLOURS | ||
125 | }; | ||
126 | |||
127 | struct game_params { | ||
128 | int w, h; | ||
129 | /* | ||
130 | * FIXME: a parameter involving degree of filling in? | ||
131 | */ | ||
132 | }; | ||
133 | |||
134 | struct game_state { | ||
135 | game_params p; | ||
136 | unsigned char *grid; | ||
137 | int px, py; | ||
138 | bool completed; | ||
139 | }; | ||
140 | |||
141 | static game_params *default_params(void) | ||
142 | { | ||
143 | game_params *ret = snew(game_params); | ||
144 | |||
145 | ret->w = 12; | ||
146 | ret->h = 10; | ||
147 | |||
148 | return ret; | ||
149 | } | ||
150 | |||
151 | static void free_params(game_params *params) | ||
152 | { | ||
153 | sfree(params); | ||
154 | } | ||
155 | |||
156 | static game_params *dup_params(const game_params *params) | ||
157 | { | ||
158 | game_params *ret = snew(game_params); | ||
159 | *ret = *params; /* structure copy */ | ||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | static const struct game_params sokoban_presets[] = { | ||
164 | { 12, 10 }, | ||
165 | { 16, 12 }, | ||
166 | { 20, 16 }, | ||
167 | }; | ||
168 | |||
169 | static bool game_fetch_preset(int i, char **name, game_params **params) | ||
170 | { | ||
171 | game_params p, *ret; | ||
172 | char *retname; | ||
173 | char namebuf[80]; | ||
174 | |||
175 | if (i < 0 || i >= lenof(sokoban_presets)) | ||
176 | return false; | ||
177 | |||
178 | p = sokoban_presets[i]; | ||
179 | ret = dup_params(&p); | ||
180 | sprintf(namebuf, "%dx%d", ret->w, ret->h); | ||
181 | retname = dupstr(namebuf); | ||
182 | |||
183 | *params = ret; | ||
184 | *name = retname; | ||
185 | return true; | ||
186 | } | ||
187 | |||
188 | static void decode_params(game_params *params, char const *string) | ||
189 | { | ||
190 | params->w = params->h = atoi(string); | ||
191 | while (*string && isdigit((unsigned char)*string)) string++; | ||
192 | if (*string == 'x') { | ||
193 | string++; | ||
194 | params->h = atoi(string); | ||
195 | } | ||
196 | } | ||
197 | |||
198 | static char *encode_params(const game_params *params, bool full) | ||
199 | { | ||
200 | char data[256]; | ||
201 | |||
202 | sprintf(data, "%dx%d", params->w, params->h); | ||
203 | |||
204 | return dupstr(data); | ||
205 | } | ||
206 | |||
207 | static config_item *game_configure(const game_params *params) | ||
208 | { | ||
209 | config_item *ret; | ||
210 | char buf[80]; | ||
211 | |||
212 | ret = snewn(3, config_item); | ||
213 | |||
214 | ret[0].name = "Width"; | ||
215 | ret[0].type = C_STRING; | ||
216 | sprintf(buf, "%d", params->w); | ||
217 | ret[0].u.string.sval = dupstr(buf); | ||
218 | |||
219 | ret[1].name = "Height"; | ||
220 | ret[1].type = C_STRING; | ||
221 | sprintf(buf, "%d", params->h); | ||
222 | ret[1].u.string.sval = dupstr(buf); | ||
223 | |||
224 | ret[2].name = NULL; | ||
225 | ret[2].type = C_END; | ||
226 | |||
227 | return ret; | ||
228 | } | ||
229 | |||
230 | static game_params *custom_params(const config_item *cfg) | ||
231 | { | ||
232 | game_params *ret = snew(game_params); | ||
233 | |||
234 | ret->w = atoi(cfg[0].u.string.sval); | ||
235 | ret->h = atoi(cfg[1].u.string.sval); | ||
236 | |||
237 | return ret; | ||
238 | } | ||
239 | |||
240 | static const char *validate_params(const game_params *params, bool full) | ||
241 | { | ||
242 | if (params->w < 4 || params->h < 4) | ||
243 | return "Width and height must both be at least 4"; | ||
244 | |||
245 | return NULL; | ||
246 | } | ||
247 | |||
248 | /* ---------------------------------------------------------------------- | ||
249 | * Game generation mechanism. | ||
250 | * | ||
251 | * To generate a Sokoban level, we begin with a completely blank | ||
252 | * grid and make valid inverse moves. Grid squares can be in a | ||
253 | * number of states. The states are: | ||
254 | * | ||
255 | * - INITIAL: this square has not as yet been touched by any | ||
256 | * inverse move, which essentially means we haven't decided what | ||
257 | * it is yet. | ||
258 | * | ||
259 | * - SPACE: this square is a space. | ||
260 | * | ||
261 | * - TARGET: this square is a space which is also the target for a | ||
262 | * barrel. | ||
263 | * | ||
264 | * - BARREL: this square contains a barrel. | ||
265 | * | ||
266 | * - BARRELTARGET: this square contains a barrel _on_ a target. | ||
267 | * | ||
268 | * - WALL: this square is a wall. | ||
269 | * | ||
270 | * - PLAYER: this square contains the player. | ||
271 | * | ||
272 | * - PLAYERTARGET: this square contains the player on a target. | ||
273 | * | ||
274 | * We begin with every square of the in state INITIAL, apart from a | ||
275 | * solid ring of WALLs around the edge. We randomly position the | ||
276 | * PLAYER somewhere. Thereafter our valid moves are: | ||
277 | * | ||
278 | * - to move the PLAYER in one direction _pulling_ a barrel after | ||
279 | * us. For this to work, we must have SPACE or INITIAL in the | ||
280 | * direction we're moving, and BARREL or BARRELTARGET in the | ||
281 | * direction we're moving away from. We leave SPACE or TARGET | ||
282 | * respectively in the vacated square. | ||
283 | * | ||
284 | * - to create a new barrel by transforming an INITIAL square into | ||
285 | * BARRELTARGET. | ||
286 | * | ||
287 | * - to move the PLAYER freely through SPACE and TARGET squares, | ||
288 | * leaving SPACE or TARGET where it started. | ||
289 | * | ||
290 | * - to move the player through INITIAL squares, carving a tunnel | ||
291 | * of SPACEs as it goes. | ||
292 | * | ||
293 | * We try to avoid destroying INITIAL squares wherever possible (if | ||
294 | * there's a path to where we want to be using only SPACE, then we | ||
295 | * should always use that). At the end of generation, every square | ||
296 | * still in state INITIAL is one which was not required at any | ||
297 | * point during generation, which means we can randomly choose | ||
298 | * whether to make it SPACE or WALL. | ||
299 | * | ||
300 | * It's unclear as yet what the right strategy for wall placement | ||
301 | * should be. Too few WALLs will yield many alternative solutions | ||
302 | * to the puzzle, whereas too many might rule out so many | ||
303 | * possibilities that the intended solution becomes obvious. | ||
304 | */ | ||
305 | |||
306 | static void sokoban_generate(int w, int h, unsigned char *grid, int moves, | ||
307 | bool nethack, random_state *rs) | ||
308 | { | ||
309 | struct pull { | ||
310 | int ox, oy, nx, ny, score; | ||
311 | }; | ||
312 | |||
313 | struct pull *pulls; | ||
314 | int *dist, *prev, *heap; | ||
315 | int x, y, px, py, i, j, d, heapsize, npulls; | ||
316 | |||
317 | pulls = snewn(w * h * 4, struct pull); | ||
318 | dist = snewn(w * h, int); | ||
319 | prev = snewn(w * h, int); | ||
320 | heap = snewn(w * h, int); | ||
321 | |||
322 | /* | ||
323 | * Configure the initial grid. | ||
324 | */ | ||
325 | for (y = 0; y < h; y++) | ||
326 | for (x = 0; x < w; x++) | ||
327 | grid[y*w+x] = (x == 0 || y == 0 || x == w-1 || y == h-1 ? | ||
328 | WALL : INITIAL); | ||
329 | if (nethack) | ||
330 | grid[1] = DEEP_PIT; | ||
331 | |||
332 | /* | ||
333 | * Place the player. | ||
334 | */ | ||
335 | i = random_upto(rs, (w-2) * (h-2)); | ||
336 | x = 1 + i % (w-2); | ||
337 | y = 1 + i / (w-2); | ||
338 | grid[y*w+x] = SPACE; | ||
339 | px = x; | ||
340 | py = y; | ||
341 | |||
342 | /* | ||
343 | * Now loop around making random inverse Sokoban moves. In this | ||
344 | * loop we aim to make one actual barrel-pull per iteration, | ||
345 | * plus as many free moves as are necessary to get into | ||
346 | * position for that pull. | ||
347 | */ | ||
348 | while (moves-- >= 0) { | ||
349 | /* | ||
350 | * First enumerate all the viable barrel-pulls we can | ||
351 | * possibly make, counting two pulls of the same barrel in | ||
352 | * different directions as different. We also include pulls | ||
353 | * we can perform by creating a new barrel. Each pull is | ||
354 | * marked with the amount of violence it would have to do | ||
355 | * to the grid. | ||
356 | */ | ||
357 | npulls = 0; | ||
358 | for (y = 0; y < h; y++) | ||
359 | for (x = 0; x < w; x++) | ||
360 | for (d = 0; d < 4; d++) { | ||
361 | int dx = DX(d); | ||
362 | int dy = DY(d); | ||
363 | int nx = x + dx, ny = y + dy; | ||
364 | int npx = nx + dx, npy = ny + dy; | ||
365 | int score = 0; | ||
366 | |||
367 | /* | ||
368 | * The candidate move is to put the player at | ||
369 | * (nx,ny), and move him to (npx,npy), pulling | ||
370 | * a barrel at (x,y) to (nx,ny). So first we | ||
371 | * must check that all those squares are within | ||
372 | * the boundaries of the grid. For this it is | ||
373 | * sufficient to check npx,npy. | ||
374 | */ | ||
375 | if (npx < 0 || npx >= w || npy < 0 || npy >= h) | ||
376 | continue; | ||
377 | |||
378 | /* | ||
379 | * (x,y) must either be a barrel, or a square | ||
380 | * which we can convert into a barrel. | ||
381 | */ | ||
382 | switch (grid[y*w+x]) { | ||
383 | case BARREL: case BARRELTARGET: | ||
384 | break; | ||
385 | case INITIAL: | ||
386 | if (nethack) | ||
387 | continue; | ||
388 | score += 10 /* new_barrel_score */; | ||
389 | break; | ||
390 | case DEEP_PIT: | ||
391 | if (!nethack) | ||
392 | continue; | ||
393 | break; | ||
394 | default: | ||
395 | continue; | ||
396 | } | ||
397 | |||
398 | /* | ||
399 | * (nx,ny) must either be a space, or a square | ||
400 | * which we can convert into a space. | ||
401 | */ | ||
402 | switch (grid[ny*w+nx]) { | ||
403 | case SPACE: case TARGET: | ||
404 | break; | ||
405 | case INITIAL: | ||
406 | score += 3 /* new_space_score */; | ||
407 | break; | ||
408 | default: | ||
409 | continue; | ||
410 | } | ||
411 | |||
412 | /* | ||
413 | * (npx,npy) must also either be a space, or a | ||
414 | * square which we can convert into a space. | ||
415 | */ | ||
416 | switch (grid[npy*w+npx]) { | ||
417 | case SPACE: case TARGET: | ||
418 | break; | ||
419 | case INITIAL: | ||
420 | score += 3 /* new_space_score */; | ||
421 | break; | ||
422 | default: | ||
423 | continue; | ||
424 | } | ||
425 | |||
426 | /* | ||
427 | * That's sufficient to tag this as a possible | ||
428 | * pull right now. We still don't know if we | ||
429 | * can reach the required player position, but | ||
430 | * that's a job for the subsequent BFS phase to | ||
431 | * tell us. | ||
432 | */ | ||
433 | pulls[npulls].ox = x; | ||
434 | pulls[npulls].oy = y; | ||
435 | pulls[npulls].nx = nx; | ||
436 | pulls[npulls].ny = ny; | ||
437 | pulls[npulls].score = score; | ||
438 | #ifdef GENERATION_DIAGNOSTICS | ||
439 | printf("found potential pull: (%d,%d)-(%d,%d) cost %d\n", | ||
440 | pulls[npulls].ox, pulls[npulls].oy, | ||
441 | pulls[npulls].nx, pulls[npulls].ny, | ||
442 | pulls[npulls].score); | ||
443 | #endif | ||
444 | npulls++; | ||
445 | } | ||
446 | #ifdef GENERATION_DIAGNOSTICS | ||
447 | printf("found %d potential pulls\n", npulls); | ||
448 | #endif | ||
449 | |||
450 | /* | ||
451 | * If there are no pulls available at all, we give up. | ||
452 | * | ||
453 | * (FIXME: or perhaps backtrack?) | ||
454 | */ | ||
455 | if (npulls == 0) | ||
456 | break; | ||
457 | |||
458 | /* | ||
459 | * Now we do a BFS from our current position, to find all | ||
460 | * the squares we can get the player into. | ||
461 | * | ||
462 | * This BFS is unusually tricky. We want to give a positive | ||
463 | * distance only to squares which we have to carve through | ||
464 | * INITIALs to get to, which means we can't just stick | ||
465 | * every square we reach on the end of our to-do list. | ||
466 | * Instead, we must maintain our list as a proper priority | ||
467 | * queue. | ||
468 | */ | ||
469 | for (i = 0; i < w*h; i++) | ||
470 | dist[i] = prev[i] = -1; | ||
471 | |||
472 | heap[0] = py*w+px; | ||
473 | heapsize = 1; | ||
474 | dist[py*w+px] = 0; | ||
475 | |||
476 | #define PARENT(n) ( ((n)-1)/2 ) | ||
477 | #define LCHILD(n) ( 2*(n)+1 ) | ||
478 | #define RCHILD(n) ( 2*(n)+2 ) | ||
479 | #define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0) | ||
480 | |||
481 | while (heapsize > 0) { | ||
482 | /* | ||
483 | * Pull the smallest element off the heap: it's at | ||
484 | * position 0. Move the arbitrary element from the very | ||
485 | * end of the heap into position 0. | ||
486 | */ | ||
487 | y = heap[0] / w; | ||
488 | x = heap[0] % w; | ||
489 | |||
490 | heapsize--; | ||
491 | heap[0] = heap[heapsize]; | ||
492 | |||
493 | /* | ||
494 | * Now repeatedly move that arbitrary element down the | ||
495 | * heap by swapping it with the more suitable of its | ||
496 | * children. | ||
497 | */ | ||
498 | i = 0; | ||
499 | while (1) { | ||
500 | int lc, rc; | ||
501 | |||
502 | lc = LCHILD(i); | ||
503 | rc = RCHILD(i); | ||
504 | |||
505 | if (lc >= heapsize) | ||
506 | break; /* we've hit bottom */ | ||
507 | |||
508 | if (rc >= heapsize) { | ||
509 | /* | ||
510 | * Special case: there is only one child to | ||
511 | * check. | ||
512 | */ | ||
513 | if (dist[heap[i]] > dist[heap[lc]]) | ||
514 | SWAP(heap[i], heap[lc]); | ||
515 | |||
516 | /* _Now_ we've hit bottom. */ | ||
517 | break; | ||
518 | } else { | ||
519 | /* | ||
520 | * The common case: there are two children and | ||
521 | * we must check them both. | ||
522 | */ | ||
523 | if (dist[heap[i]] > dist[heap[lc]] || | ||
524 | dist[heap[i]] > dist[heap[rc]]) { | ||
525 | /* | ||
526 | * Pick the more appropriate child to swap with | ||
527 | * (i.e. the one which would want to be the | ||
528 | * parent if one were above the other - as one | ||
529 | * is about to be). | ||
530 | */ | ||
531 | if (dist[heap[lc]] > dist[heap[rc]]) { | ||
532 | SWAP(heap[i], heap[rc]); | ||
533 | i = rc; | ||
534 | } else { | ||
535 | SWAP(heap[i], heap[lc]); | ||
536 | i = lc; | ||
537 | } | ||
538 | } else { | ||
539 | /* This element is in the right place; we're done. */ | ||
540 | break; | ||
541 | } | ||
542 | } | ||
543 | } | ||
544 | |||
545 | /* | ||
546 | * OK, that's given us (x,y) for this phase of the | ||
547 | * search. Now try all directions from here. | ||
548 | */ | ||
549 | |||
550 | for (d = 0; d < 4; d++) { | ||
551 | int dx = DX(d); | ||
552 | int dy = DY(d); | ||
553 | int nx = x + dx, ny = y + dy; | ||
554 | if (nx < 0 || nx >= w || ny < 0 || ny >= h) | ||
555 | continue; | ||
556 | if (grid[ny*w+nx] != SPACE && grid[ny*w+nx] != TARGET && | ||
557 | grid[ny*w+nx] != INITIAL) | ||
558 | continue; | ||
559 | if (dist[ny*w+nx] == -1) { | ||
560 | dist[ny*w+nx] = dist[y*w+x] + (grid[ny*w+nx] == INITIAL); | ||
561 | prev[ny*w+nx] = y*w+x; | ||
562 | |||
563 | /* | ||
564 | * Now insert ny*w+nx at the end of the heap, | ||
565 | * and move it down to its appropriate resting | ||
566 | * place. | ||
567 | */ | ||
568 | i = heapsize; | ||
569 | heap[heapsize++] = ny*w+nx; | ||
570 | |||
571 | /* | ||
572 | * Swap element n with its parent repeatedly to | ||
573 | * preserve the heap property. | ||
574 | */ | ||
575 | |||
576 | while (i > 0) { | ||
577 | int p = PARENT(i); | ||
578 | |||
579 | if (dist[heap[p]] > dist[heap[i]]) { | ||
580 | SWAP(heap[p], heap[i]); | ||
581 | i = p; | ||
582 | } else | ||
583 | break; | ||
584 | } | ||
585 | } | ||
586 | } | ||
587 | } | ||
588 | |||
589 | #undef PARENT | ||
590 | #undef LCHILD | ||
591 | #undef RCHILD | ||
592 | #undef SWAP | ||
593 | |||
594 | #ifdef GENERATION_DIAGNOSTICS | ||
595 | printf("distance map:\n"); | ||
596 | for (i = 0; i < h; i++) { | ||
597 | for (j = 0; j < w; j++) { | ||
598 | int d = dist[i*w+j]; | ||
599 | int c; | ||
600 | if (d < 0) | ||
601 | c = '#'; | ||
602 | else if (d >= 36) | ||
603 | c = '!'; | ||
604 | else if (d >= 10) | ||
605 | c = 'A' - 10 + d; | ||
606 | else | ||
607 | c = '0' + d; | ||
608 | putchar(c); | ||
609 | } | ||
610 | putchar('\n'); | ||
611 | } | ||
612 | #endif | ||
613 | |||
614 | /* | ||
615 | * Now we can go back through the `pulls' array, adjusting | ||
616 | * the score for each pull depending on how hard it is to | ||
617 | * reach its starting point, and also throwing out any | ||
618 | * whose starting points are genuinely unreachable even | ||
619 | * with the possibility of carving through INITIAL squares. | ||
620 | */ | ||
621 | for (i = j = 0; i < npulls; i++) { | ||
622 | #ifdef GENERATION_DIAGNOSTICS | ||
623 | printf("potential pull (%d,%d)-(%d,%d)", | ||
624 | pulls[i].ox, pulls[i].oy, | ||
625 | pulls[i].nx, pulls[i].ny); | ||
626 | #endif | ||
627 | x = pulls[i].nx; | ||
628 | y = pulls[i].ny; | ||
629 | if (dist[y*w+x] < 0) { | ||
630 | #ifdef GENERATION_DIAGNOSTICS | ||
631 | printf(" unreachable\n"); | ||
632 | #endif | ||
633 | continue; /* this pull isn't feasible at all */ | ||
634 | } else { | ||
635 | /* | ||
636 | * Another nasty special case we have to check is | ||
637 | * whether the initial barrel location (ox,oy) is | ||
638 | * on the path used to reach the square. This can | ||
639 | * occur if that square is in state INITIAL: the | ||
640 | * pull is initially considered valid on the basis | ||
641 | * that the INITIAL can become BARRELTARGET, and | ||
642 | * it's also considered reachable on the basis that | ||
643 | * INITIAL can be turned into SPACE, but it can't | ||
644 | * be both at once. | ||
645 | * | ||
646 | * Fortunately, if (ox,oy) is on the path at all, | ||
647 | * it must be only one space from the end, so this | ||
648 | * is easy to spot and rule out. | ||
649 | */ | ||
650 | if (prev[y*w+x] == pulls[i].oy*w+pulls[i].ox) { | ||
651 | #ifdef GENERATION_DIAGNOSTICS | ||
652 | printf(" goes through itself\n"); | ||
653 | #endif | ||
654 | continue; /* this pull isn't feasible at all */ | ||
655 | } | ||
656 | pulls[j] = pulls[i]; /* structure copy */ | ||
657 | pulls[j].score += dist[y*w+x] * 3 /* new_space_score */; | ||
658 | #ifdef GENERATION_DIAGNOSTICS | ||
659 | printf(" reachable at distance %d (cost now %d)\n", | ||
660 | dist[y*w+x], pulls[j].score); | ||
661 | #endif | ||
662 | j++; | ||
663 | } | ||
664 | } | ||
665 | npulls = j; | ||
666 | |||
667 | /* | ||
668 | * Again, if there are no pulls available at all, we give | ||
669 | * up. | ||
670 | * | ||
671 | * (FIXME: or perhaps backtrack?) | ||
672 | */ | ||
673 | if (npulls == 0) | ||
674 | break; | ||
675 | |||
676 | /* | ||
677 | * Now choose which pull to make. On the one hand we should | ||
678 | * prefer pulls which do less damage to the INITIAL squares | ||
679 | * (thus, ones for which we can already get into position | ||
680 | * via existing SPACEs, and for which the barrel already | ||
681 | * exists and doesn't have to be invented); on the other, | ||
682 | * we want to avoid _always_ preferring such pulls, on the | ||
683 | * grounds that that will lead to levels without very much | ||
684 | * stuff in. | ||
685 | * | ||
686 | * When creating new barrels, we prefer creations which are | ||
687 | * next to existing TARGET squares. | ||
688 | * | ||
689 | * FIXME: for the moment I'll make this very simple indeed. | ||
690 | */ | ||
691 | i = random_upto(rs, npulls); | ||
692 | |||
693 | /* | ||
694 | * Actually make the pull, including carving a path to get | ||
695 | * to the site if necessary. | ||
696 | */ | ||
697 | x = pulls[i].nx; | ||
698 | y = pulls[i].ny; | ||
699 | while (prev[y*w+x] >= 0) { | ||
700 | int p; | ||
701 | |||
702 | if (grid[y*w+x] == INITIAL) | ||
703 | grid[y*w+x] = SPACE; | ||
704 | |||
705 | p = prev[y*w+x]; | ||
706 | y = p / w; | ||
707 | x = p % w; | ||
708 | } | ||
709 | px = 2*pulls[i].nx - pulls[i].ox; | ||
710 | py = 2*pulls[i].ny - pulls[i].oy; | ||
711 | if (grid[py*w+px] == INITIAL) | ||
712 | grid[py*w+px] = SPACE; | ||
713 | if (grid[pulls[i].ny*w+pulls[i].nx] == TARGET) | ||
714 | grid[pulls[i].ny*w+pulls[i].nx] = BARRELTARGET; | ||
715 | else | ||
716 | grid[pulls[i].ny*w+pulls[i].nx] = BARREL; | ||
717 | if (grid[pulls[i].oy*w+pulls[i].ox] == BARREL) | ||
718 | grid[pulls[i].oy*w+pulls[i].ox] = SPACE; | ||
719 | else if (grid[pulls[i].oy*w+pulls[i].ox] != DEEP_PIT) | ||
720 | grid[pulls[i].oy*w+pulls[i].ox] = TARGET; | ||
721 | } | ||
722 | |||
723 | sfree(heap); | ||
724 | sfree(prev); | ||
725 | sfree(dist); | ||
726 | sfree(pulls); | ||
727 | |||
728 | if (grid[py*w+px] == TARGET) | ||
729 | grid[py*w+px] = PLAYERTARGET; | ||
730 | else | ||
731 | grid[py*w+px] = PLAYER; | ||
732 | } | ||
733 | |||
734 | static char *new_game_desc(const game_params *params, random_state *rs, | ||
735 | char **aux, bool interactive) | ||
736 | { | ||
737 | int w = params->w, h = params->h; | ||
738 | char *desc; | ||
739 | int desclen, descpos, descsize, prev, count; | ||
740 | unsigned char *grid; | ||
741 | int i, j; | ||
742 | |||
743 | /* | ||
744 | * FIXME: perhaps some more interesting means of choosing how | ||
745 | * many moves to try? | ||
746 | */ | ||
747 | grid = snewn(w*h, unsigned char); | ||
748 | sokoban_generate(w, h, grid, w*h, false, rs); | ||
749 | |||
750 | desclen = descpos = descsize = 0; | ||
751 | desc = NULL; | ||
752 | prev = -1; | ||
753 | count = 0; | ||
754 | for (i = 0; i < w*h; i++) { | ||
755 | if (descsize < desclen + 40) { | ||
756 | descsize = desclen + 100; | ||
757 | desc = sresize(desc, descsize, char); | ||
758 | desc[desclen] = '\0'; | ||
759 | } | ||
760 | switch (grid[i]) { | ||
761 | case INITIAL: | ||
762 | j = 'w'; /* FIXME: make some of these 's'? */ | ||
763 | break; | ||
764 | case SPACE: | ||
765 | j = 's'; | ||
766 | break; | ||
767 | case WALL: | ||
768 | j = 'w'; | ||
769 | break; | ||
770 | case TARGET: | ||
771 | j = 't'; | ||
772 | break; | ||
773 | case BARREL: | ||
774 | j = 'b'; | ||
775 | break; | ||
776 | case BARRELTARGET: | ||
777 | j = 'f'; | ||
778 | break; | ||
779 | case DEEP_PIT: | ||
780 | j = 'd'; | ||
781 | break; | ||
782 | case PLAYER: | ||
783 | j = 'u'; | ||
784 | break; | ||
785 | case PLAYERTARGET: | ||
786 | j = 'v'; | ||
787 | break; | ||
788 | default: | ||
789 | j = '?'; | ||
790 | break; | ||
791 | } | ||
792 | assert(j != '?'); | ||
793 | if (j != prev) { | ||
794 | desc[desclen++] = j; | ||
795 | descpos = desclen; | ||
796 | prev = j; | ||
797 | count = 1; | ||
798 | } else { | ||
799 | count++; | ||
800 | desclen = descpos + sprintf(desc+descpos, "%d", count); | ||
801 | } | ||
802 | } | ||
803 | |||
804 | sfree(grid); | ||
805 | |||
806 | return desc; | ||
807 | } | ||
808 | |||
809 | static const char *validate_desc(const game_params *params, const char *desc) | ||
810 | { | ||
811 | int w = params->w, h = params->h; | ||
812 | int area = 0; | ||
813 | int nplayers = 0; | ||
814 | |||
815 | while (*desc) { | ||
816 | int c = *desc++; | ||
817 | int n = 1; | ||
818 | if (*desc && isdigit((unsigned char)*desc)) { | ||
819 | n = atoi(desc); | ||
820 | while (*desc && isdigit((unsigned char)*desc)) desc++; | ||
821 | } | ||
822 | |||
823 | area += n; | ||
824 | |||
825 | if (c == PLAYER || c == PLAYERTARGET) | ||
826 | nplayers += n; | ||
827 | else if (c == INITIAL || c == SPACE || c == WALL || c == TARGET || | ||
828 | c == PIT || c == DEEP_PIT || IS_BARREL(c)) | ||
829 | /* ok */; | ||
830 | else | ||
831 | return "Invalid character in game description"; | ||
832 | } | ||
833 | |||
834 | if (area > w*h) | ||
835 | return "Too much data in game description"; | ||
836 | if (area < w*h) | ||
837 | return "Too little data in game description"; | ||
838 | if (nplayers < 1) | ||
839 | return "No starting player position specified"; | ||
840 | if (nplayers > 1) | ||
841 | return "More than one starting player position specified"; | ||
842 | |||
843 | return NULL; | ||
844 | } | ||
845 | |||
846 | static game_state *new_game(midend *me, const game_params *params, | ||
847 | const char *desc) | ||
848 | { | ||
849 | int w = params->w, h = params->h; | ||
850 | game_state *state = snew(game_state); | ||
851 | int i; | ||
852 | |||
853 | state->p = *params; /* structure copy */ | ||
854 | state->grid = snewn(w*h, unsigned char); | ||
855 | state->px = state->py = -1; | ||
856 | state->completed = false; | ||
857 | |||
858 | i = 0; | ||
859 | |||
860 | while (*desc) { | ||
861 | int c = *desc++; | ||
862 | int n = 1; | ||
863 | if (*desc && isdigit((unsigned char)*desc)) { | ||
864 | n = atoi(desc); | ||
865 | while (*desc && isdigit((unsigned char)*desc)) desc++; | ||
866 | } | ||
867 | |||
868 | if (c == PLAYER || c == PLAYERTARGET) { | ||
869 | state->py = i / w; | ||
870 | state->px = i % w; | ||
871 | c = IS_ON_TARGET(c) ? TARGET : SPACE; | ||
872 | } | ||
873 | |||
874 | while (n-- > 0) | ||
875 | state->grid[i++] = c; | ||
876 | } | ||
877 | |||
878 | assert(i == w*h); | ||
879 | assert(state->px != -1 && state->py != -1); | ||
880 | |||
881 | return state; | ||
882 | } | ||
883 | |||
884 | static game_state *dup_game(const game_state *state) | ||
885 | { | ||
886 | int w = state->p.w, h = state->p.h; | ||
887 | game_state *ret = snew(game_state); | ||
888 | |||
889 | ret->p = state->p; /* structure copy */ | ||
890 | ret->grid = snewn(w*h, unsigned char); | ||
891 | memcpy(ret->grid, state->grid, w*h); | ||
892 | ret->px = state->px; | ||
893 | ret->py = state->py; | ||
894 | ret->completed = state->completed; | ||
895 | |||
896 | return ret; | ||
897 | } | ||
898 | |||
899 | static void free_game(game_state *state) | ||
900 | { | ||
901 | sfree(state->grid); | ||
902 | sfree(state); | ||
903 | } | ||
904 | |||
905 | static char *solve_game(const game_state *state, const game_state *currstate, | ||
906 | const char *aux, const char **error) | ||
907 | { | ||
908 | return NULL; | ||
909 | } | ||
910 | |||
911 | static bool game_can_format_as_text_now(const game_params *params) | ||
912 | { | ||
913 | return true; | ||
914 | } | ||
915 | |||
916 | static char *game_text_format(const game_state *state) | ||
917 | { | ||
918 | return NULL; | ||
919 | } | ||
920 | |||
921 | static game_ui *new_ui(const game_state *state) | ||
922 | { | ||
923 | return NULL; | ||
924 | } | ||
925 | |||
926 | static void free_ui(game_ui *ui) | ||
927 | { | ||
928 | } | ||
929 | |||
930 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | ||
931 | const game_state *newstate) | ||
932 | { | ||
933 | } | ||
934 | |||
935 | struct game_drawstate { | ||
936 | game_params p; | ||
937 | int tilesize; | ||
938 | bool started; | ||
939 | unsigned short *grid; | ||
940 | }; | ||
941 | |||
942 | #define PREFERRED_TILESIZE 32 | ||
943 | #define TILESIZE (ds->tilesize) | ||
944 | #define BORDER (TILESIZE) | ||
945 | #define HIGHLIGHT_WIDTH (TILESIZE / 10) | ||
946 | #define COORD(x) ( (x) * TILESIZE + BORDER ) | ||
947 | #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) | ||
948 | |||
949 | /* | ||
950 | * I'm going to need to do most of the move-type analysis in both | ||
951 | * interpret_move and execute_move, so I'll abstract it out into a | ||
952 | * subfunction. move_type() returns -1 for an illegal move, 0 for a | ||
953 | * movement, and 1 for a push. | ||
954 | */ | ||
955 | static int move_type(const game_state *state, int dx, int dy) | ||
956 | { | ||
957 | int w = state->p.w, h = state->p.h; | ||
958 | int px = state->px, py = state->py; | ||
959 | int nx, ny, nbx, nby; | ||
960 | |||
961 | assert(dx >= -1 && dx <= +1); | ||
962 | assert(dy >= -1 && dy <= +1); | ||
963 | assert(dx || dy); | ||
964 | |||
965 | nx = px + dx; | ||
966 | ny = py + dy; | ||
967 | |||
968 | /* | ||
969 | * Disallow any move that goes off the grid. | ||
970 | */ | ||
971 | if (nx < 0 || nx >= w || ny < 0 || ny >= h) | ||
972 | return -1; | ||
973 | |||
974 | /* | ||
975 | * Examine the target square of the move to see whether it's a | ||
976 | * space, a barrel, or a wall. | ||
977 | */ | ||
978 | |||
979 | if (state->grid[ny*w+nx] == WALL || | ||
980 | state->grid[ny*w+nx] == PIT || | ||
981 | state->grid[ny*w+nx] == DEEP_PIT) | ||
982 | return -1; /* this one's easy; just disallow it */ | ||
983 | |||
984 | if (IS_BARREL(state->grid[ny*w+nx])) { | ||
985 | /* | ||
986 | * This is a push move. For a start, that means it must not | ||
987 | * be diagonal. | ||
988 | */ | ||
989 | if (dy && dx) | ||
990 | return -1; | ||
991 | |||
992 | /* | ||
993 | * Now find the location of the third square involved in | ||
994 | * the push, and stop if it's off the edge. | ||
995 | */ | ||
996 | nbx = nx + dx; | ||
997 | nby = ny + dy; | ||
998 | if (nbx < 0 || nbx >= w || nby < 0 || nby >= h) | ||
999 | return -1; | ||
1000 | |||
1001 | /* | ||
1002 | * That third square must be able to accept a barrel. | ||
1003 | */ | ||
1004 | if (state->grid[nby*w+nbx] == SPACE || | ||
1005 | state->grid[nby*w+nbx] == TARGET || | ||
1006 | state->grid[nby*w+nbx] == PIT || | ||
1007 | state->grid[nby*w+nbx] == DEEP_PIT) { | ||
1008 | /* | ||
1009 | * The push is valid. | ||
1010 | */ | ||
1011 | return 1; | ||
1012 | } else { | ||
1013 | return -1; | ||
1014 | } | ||
1015 | } else { | ||
1016 | /* | ||
1017 | * This is just an ordinary move. We've already checked the | ||
1018 | * target square, so the only thing left to check is that a | ||
1019 | * diagonal move has a space on one side to have notionally | ||
1020 | * gone through. | ||
1021 | */ | ||
1022 | if (dx && dy && | ||
1023 | state->grid[(py+dy)*w+px] != SPACE && | ||
1024 | state->grid[(py+dy)*w+px] != TARGET && | ||
1025 | state->grid[py*w+(px+dx)] != SPACE && | ||
1026 | state->grid[py*w+(px+dx)] != TARGET) | ||
1027 | return -1; | ||
1028 | |||
1029 | /* | ||
1030 | * Otherwise, the move is valid. | ||
1031 | */ | ||
1032 | return 0; | ||
1033 | } | ||
1034 | } | ||
1035 | |||
1036 | static char *interpret_move(const game_state *state, game_ui *ui, | ||
1037 | const game_drawstate *ds, | ||
1038 | int x, int y, int button) | ||
1039 | { | ||
1040 | int dx=0, dy=0; | ||
1041 | char *move; | ||
1042 | |||
1043 | /* | ||
1044 | * Diagonal movement is supported as it is in NetHack: it's | ||
1045 | * for movement only (never pushing), and one of the two | ||
1046 | * squares adjacent to both the source and destination | ||
1047 | * squares must be free to move through. In other words, it | ||
1048 | * is only a shorthand for two orthogonal moves and cannot | ||
1049 | * change the nature of the actual puzzle game. | ||
1050 | */ | ||
1051 | if (button == CURSOR_UP || button == (MOD_NUM_KEYPAD | '8')) | ||
1052 | dx = 0, dy = -1; | ||
1053 | else if (button == CURSOR_DOWN || button == (MOD_NUM_KEYPAD | '2')) | ||
1054 | dx = 0, dy = +1; | ||
1055 | else if (button == CURSOR_LEFT || button == (MOD_NUM_KEYPAD | '4')) | ||
1056 | dx = -1, dy = 0; | ||
1057 | else if (button == CURSOR_RIGHT || button == (MOD_NUM_KEYPAD | '6')) | ||
1058 | dx = +1, dy = 0; | ||
1059 | else if (button == (MOD_NUM_KEYPAD | '7')) | ||
1060 | dx = -1, dy = -1; | ||
1061 | else if (button == (MOD_NUM_KEYPAD | '9')) | ||
1062 | dx = +1, dy = -1; | ||
1063 | else if (button == (MOD_NUM_KEYPAD | '1')) | ||
1064 | dx = -1, dy = +1; | ||
1065 | else if (button == (MOD_NUM_KEYPAD | '3')) | ||
1066 | dx = +1, dy = +1; | ||
1067 | else if (button == LEFT_BUTTON) | ||
1068 | { | ||
1069 | if(x < COORD(state->px)) | ||
1070 | dx = -1; | ||
1071 | else if (x > COORD(state->px + 1)) | ||
1072 | dx = 1; | ||
1073 | if(y < COORD(state->py)) | ||
1074 | dy = -1; | ||
1075 | else if (y > COORD(state->py + 1)) | ||
1076 | dy = 1; | ||
1077 | } | ||
1078 | else | ||
1079 | return NULL; | ||
1080 | |||
1081 | if((dx == 0) && (dy == 0)) | ||
1082 | return(NULL); | ||
1083 | |||
1084 | if (move_type(state, dx, dy) < 0) | ||
1085 | return NULL; | ||
1086 | |||
1087 | move = snewn(2, char); | ||
1088 | move[1] = '\0'; | ||
1089 | move[0] = '5' - 3*dy + dx; | ||
1090 | return move; | ||
1091 | } | ||
1092 | |||
1093 | static game_state *execute_move(const game_state *state, const char *move) | ||
1094 | { | ||
1095 | int w = state->p.w, h = state->p.h; | ||
1096 | int px = state->px, py = state->py; | ||
1097 | int dx, dy, nx, ny, nbx, nby, type, m, i; | ||
1098 | bool freebarrels, freetargets; | ||
1099 | game_state *ret; | ||
1100 | |||
1101 | if (*move < '1' || *move == '5' || *move > '9' || move[1]) | ||
1102 | return NULL; /* invalid move string */ | ||
1103 | |||
1104 | m = *move - '0'; | ||
1105 | dx = (m + 2) % 3 - 1; | ||
1106 | dy = 2 - (m + 2) / 3; | ||
1107 | type = move_type(state, dx, dy); | ||
1108 | if (type < 0) | ||
1109 | return NULL; | ||
1110 | |||
1111 | ret = dup_game(state); | ||
1112 | |||
1113 | nx = px + dx; | ||
1114 | ny = py + dy; | ||
1115 | nbx = nx + dx; | ||
1116 | nby = ny + dy; | ||
1117 | |||
1118 | if (type) { | ||
1119 | int b; | ||
1120 | |||
1121 | /* | ||
1122 | * Push. | ||
1123 | */ | ||
1124 | b = ret->grid[ny*w+nx]; | ||
1125 | if (IS_ON_TARGET(b)) { | ||
1126 | ret->grid[ny*w+nx] = TARGET; | ||
1127 | b = DETARGETISE(b); | ||
1128 | } else | ||
1129 | ret->grid[ny*w+nx] = SPACE; | ||
1130 | |||
1131 | if (ret->grid[nby*w+nbx] == PIT) | ||
1132 | ret->grid[nby*w+nbx] = SPACE; | ||
1133 | else if (ret->grid[nby*w+nbx] == DEEP_PIT) | ||
1134 | /* do nothing - the pit eats the barrel and remains there */; | ||
1135 | else if (ret->grid[nby*w+nbx] == TARGET) | ||
1136 | ret->grid[nby*w+nbx] = TARGETISE(b); | ||
1137 | else | ||
1138 | ret->grid[nby*w+nbx] = b; | ||
1139 | } | ||
1140 | |||
1141 | ret->px = nx; | ||
1142 | ret->py = ny; | ||
1143 | |||
1144 | /* | ||
1145 | * Check for completion. This is surprisingly complicated, | ||
1146 | * given the presence of pits and deep pits, and also the fact | ||
1147 | * that some Sokoban levels with pits have fewer pits than | ||
1148 | * barrels (due to providing spares, e.g. NetHack's). I think | ||
1149 | * the completion condition in fact must be that the game | ||
1150 | * cannot become any _more_ complete. That is, _either_ there | ||
1151 | * are no remaining barrels not on targets, _or_ there is a | ||
1152 | * good reason why any such barrels cannot be placed. The only | ||
1153 | * available good reason is that there are no remaining pits, | ||
1154 | * no free target squares, and no deep pits at all. | ||
1155 | */ | ||
1156 | if (!ret->completed) { | ||
1157 | freebarrels = false; | ||
1158 | freetargets = false; | ||
1159 | for (i = 0; i < w*h; i++) { | ||
1160 | int v = ret->grid[i]; | ||
1161 | |||
1162 | if (IS_BARREL(v) && !IS_ON_TARGET(v)) | ||
1163 | freebarrels = true; | ||
1164 | if (v == DEEP_PIT || v == PIT || | ||
1165 | (!IS_BARREL(v) && IS_ON_TARGET(v))) | ||
1166 | freetargets = true; | ||
1167 | } | ||
1168 | |||
1169 | if (!freebarrels || !freetargets) | ||
1170 | ret->completed = true; | ||
1171 | } | ||
1172 | |||
1173 | return ret; | ||
1174 | } | ||
1175 | |||
1176 | /* ---------------------------------------------------------------------- | ||
1177 | * Drawing routines. | ||
1178 | */ | ||
1179 | |||
1180 | static void game_compute_size(const game_params *params, int tilesize, | ||
1181 | const game_ui *ui, int *x, int *y) | ||
1182 | { | ||
1183 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ | ||
1184 | struct { int tilesize; } ads, *ds = &ads; | ||
1185 | ads.tilesize = tilesize; | ||
1186 | |||
1187 | *x = 2 * BORDER + 1 + params->w * TILESIZE; | ||
1188 | *y = 2 * BORDER + 1 + params->h * TILESIZE; | ||
1189 | } | ||
1190 | |||
1191 | static void game_set_size(drawing *dr, game_drawstate *ds, | ||
1192 | const game_params *params, int tilesize) | ||
1193 | { | ||
1194 | ds->tilesize = tilesize; | ||
1195 | } | ||
1196 | |||
1197 | static float *game_colours(frontend *fe, int *ncolours) | ||
1198 | { | ||
1199 | float *ret = snewn(3 * NCOLOURS, float); | ||
1200 | int i; | ||
1201 | |||
1202 | game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); | ||
1203 | |||
1204 | ret[COL_OUTLINE * 3 + 0] = 0.0F; | ||
1205 | ret[COL_OUTLINE * 3 + 1] = 0.0F; | ||
1206 | ret[COL_OUTLINE * 3 + 2] = 0.0F; | ||
1207 | |||
1208 | ret[COL_PLAYER * 3 + 0] = 0.0F; | ||
1209 | ret[COL_PLAYER * 3 + 1] = 1.0F; | ||
1210 | ret[COL_PLAYER * 3 + 2] = 0.0F; | ||
1211 | |||
1212 | ret[COL_BARREL * 3 + 0] = 0.6F; | ||
1213 | ret[COL_BARREL * 3 + 1] = 0.3F; | ||
1214 | ret[COL_BARREL * 3 + 2] = 0.0F; | ||
1215 | |||
1216 | ret[COL_TARGET * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0]; | ||
1217 | ret[COL_TARGET * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1]; | ||
1218 | ret[COL_TARGET * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2]; | ||
1219 | |||
1220 | ret[COL_PIT * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0] / 2; | ||
1221 | ret[COL_PIT * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1] / 2; | ||
1222 | ret[COL_PIT * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2] / 2; | ||
1223 | |||
1224 | ret[COL_DEEP_PIT * 3 + 0] = 0.0F; | ||
1225 | ret[COL_DEEP_PIT * 3 + 1] = 0.0F; | ||
1226 | ret[COL_DEEP_PIT * 3 + 2] = 0.0F; | ||
1227 | |||
1228 | ret[COL_TEXT * 3 + 0] = 1.0F; | ||
1229 | ret[COL_TEXT * 3 + 1] = 1.0F; | ||
1230 | ret[COL_TEXT * 3 + 2] = 1.0F; | ||
1231 | |||
1232 | ret[COL_GRID * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0]; | ||
1233 | ret[COL_GRID * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1]; | ||
1234 | ret[COL_GRID * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2]; | ||
1235 | |||
1236 | ret[COL_OUTLINE * 3 + 0] = 0.0F; | ||
1237 | ret[COL_OUTLINE * 3 + 1] = 0.0F; | ||
1238 | ret[COL_OUTLINE * 3 + 2] = 0.0F; | ||
1239 | |||
1240 | for (i = 0; i < 3; i++) { | ||
1241 | ret[COL_WALL * 3 + i] = (3 * ret[COL_BACKGROUND * 3 + i] + | ||
1242 | 1 * ret[COL_HIGHLIGHT * 3 + i]) / 4; | ||
1243 | } | ||
1244 | |||
1245 | *ncolours = NCOLOURS; | ||
1246 | return ret; | ||
1247 | } | ||
1248 | |||
1249 | static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) | ||
1250 | { | ||
1251 | int w = state->p.w, h = state->p.h; | ||
1252 | struct game_drawstate *ds = snew(struct game_drawstate); | ||
1253 | int i; | ||
1254 | |||
1255 | ds->tilesize = 0; | ||
1256 | ds->p = state->p; /* structure copy */ | ||
1257 | ds->grid = snewn(w*h, unsigned short); | ||
1258 | for (i = 0; i < w*h; i++) | ||
1259 | ds->grid[i] = INVALID; | ||
1260 | ds->started = false; | ||
1261 | |||
1262 | return ds; | ||
1263 | } | ||
1264 | |||
1265 | static void game_free_drawstate(drawing *dr, game_drawstate *ds) | ||
1266 | { | ||
1267 | sfree(ds->grid); | ||
1268 | sfree(ds); | ||
1269 | } | ||
1270 | |||
1271 | static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, int v) | ||
1272 | { | ||
1273 | int tx = COORD(x), ty = COORD(y); | ||
1274 | int bg = (v & 0x100 ? COL_HIGHLIGHT : COL_BACKGROUND); | ||
1275 | |||
1276 | v &= 0xFF; | ||
1277 | |||
1278 | clip(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1); | ||
1279 | draw_rect(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1, bg); | ||
1280 | |||
1281 | if (v == WALL) { | ||
1282 | int coords[6]; | ||
1283 | |||
1284 | coords[0] = tx + TILESIZE; | ||
1285 | coords[1] = ty + TILESIZE; | ||
1286 | coords[2] = tx + TILESIZE; | ||
1287 | coords[3] = ty + 1; | ||
1288 | coords[4] = tx + 1; | ||
1289 | coords[5] = ty + TILESIZE; | ||
1290 | draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT); | ||
1291 | |||
1292 | coords[0] = tx + 1; | ||
1293 | coords[1] = ty + 1; | ||
1294 | draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); | ||
1295 | |||
1296 | draw_rect(dr, tx + 1 + HIGHLIGHT_WIDTH, ty + 1 + HIGHLIGHT_WIDTH, | ||
1297 | TILESIZE - 2*HIGHLIGHT_WIDTH, | ||
1298 | TILESIZE - 2*HIGHLIGHT_WIDTH, COL_WALL); | ||
1299 | } else if (v == PIT) { | ||
1300 | draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, | ||
1301 | TILESIZE*3/7, COL_PIT, COL_OUTLINE); | ||
1302 | } else if (v == DEEP_PIT) { | ||
1303 | draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, | ||
1304 | TILESIZE*3/7, COL_DEEP_PIT, COL_OUTLINE); | ||
1305 | } else { | ||
1306 | if (IS_ON_TARGET(v)) { | ||
1307 | draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, | ||
1308 | TILESIZE*3/7, COL_TARGET, COL_OUTLINE); | ||
1309 | } | ||
1310 | if (IS_PLAYER(v)) { | ||
1311 | draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, | ||
1312 | TILESIZE/3, COL_PLAYER, COL_OUTLINE); | ||
1313 | } else if (IS_BARREL(v)) { | ||
1314 | char str[2]; | ||
1315 | |||
1316 | draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, | ||
1317 | TILESIZE/3, COL_BARREL, COL_OUTLINE); | ||
1318 | str[1] = '\0'; | ||
1319 | str[0] = BARREL_LABEL(v); | ||
1320 | if (str[0]) { | ||
1321 | draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2, | ||
1322 | FONT_VARIABLE, TILESIZE/2, | ||
1323 | ALIGN_VCENTRE | ALIGN_HCENTRE, COL_TEXT, str); | ||
1324 | } | ||
1325 | } | ||
1326 | } | ||
1327 | |||
1328 | unclip(dr); | ||
1329 | draw_update(dr, tx, ty, TILESIZE, TILESIZE); | ||
1330 | } | ||
1331 | |||
1332 | static void game_redraw(drawing *dr, game_drawstate *ds, | ||
1333 | const game_state *oldstate, const game_state *state, | ||
1334 | int dir, const game_ui *ui, | ||
1335 | float animtime, float flashtime) | ||
1336 | { | ||
1337 | int w = state->p.w, h = state->p.h /*, wh = w*h */; | ||
1338 | int x, y; | ||
1339 | int flashtype; | ||
1340 | |||
1341 | if (flashtime && | ||
1342 | !((int)(flashtime * 3 / FLASH_LENGTH) % 2)) | ||
1343 | flashtype = 0x100; | ||
1344 | else | ||
1345 | flashtype = 0; | ||
1346 | |||
1347 | /* | ||
1348 | * Initialise a fresh drawstate. | ||
1349 | */ | ||
1350 | if (!ds->started) { | ||
1351 | /* | ||
1352 | * Draw the grid lines. | ||
1353 | */ | ||
1354 | for (y = 0; y <= h; y++) | ||
1355 | draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), | ||
1356 | COL_LOWLIGHT); | ||
1357 | for (x = 0; x <= w; x++) | ||
1358 | draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), | ||
1359 | COL_LOWLIGHT); | ||
1360 | |||
1361 | ds->started = true; | ||
1362 | } | ||
1363 | |||
1364 | /* | ||
1365 | * Draw the grid contents. | ||
1366 | */ | ||
1367 | for (y = 0; y < h; y++) | ||
1368 | for (x = 0; x < w; x++) { | ||
1369 | int v = state->grid[y*w+x]; | ||
1370 | if (y == state->py && x == state->px) { | ||
1371 | if (v == TARGET) | ||
1372 | v = PLAYERTARGET; | ||
1373 | else { | ||
1374 | assert(v == SPACE); | ||
1375 | v = PLAYER; | ||
1376 | } | ||
1377 | } | ||
1378 | |||
1379 | v |= flashtype; | ||
1380 | |||
1381 | if (ds->grid[y*w+x] != v) { | ||
1382 | draw_tile(dr, ds, x, y, v); | ||
1383 | ds->grid[y*w+x] = v; | ||
1384 | } | ||
1385 | } | ||
1386 | |||
1387 | } | ||
1388 | |||
1389 | static float game_anim_length(const game_state *oldstate, | ||
1390 | const game_state *newstate, int dir, game_ui *ui) | ||
1391 | { | ||
1392 | return 0.0F; | ||
1393 | } | ||
1394 | |||
1395 | static float game_flash_length(const game_state *oldstate, | ||
1396 | const game_state *newstate, int dir, game_ui *ui) | ||
1397 | { | ||
1398 | if (!oldstate->completed && newstate->completed) | ||
1399 | return FLASH_LENGTH; | ||
1400 | else | ||
1401 | return 0.0F; | ||
1402 | } | ||
1403 | |||
1404 | static void game_get_cursor_location(const game_ui *ui, | ||
1405 | const game_drawstate *ds, | ||
1406 | const game_state *state, | ||
1407 | const game_params *params, | ||
1408 | int *x, int *y, int *w, int *h) | ||
1409 | { | ||
1410 | } | ||
1411 | |||
1412 | static int game_status(const game_state *state) | ||
1413 | { | ||
1414 | return state->completed ? +1 : 0; | ||
1415 | } | ||
1416 | |||
1417 | static bool game_timing_state(const game_state *state, game_ui *ui) | ||
1418 | { | ||
1419 | return true; | ||
1420 | } | ||
1421 | |||
1422 | static void game_print_size(const game_params *params, const game_ui *ui, | ||
1423 | float *x, float *y) | ||
1424 | { | ||
1425 | } | ||
1426 | |||
1427 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, | ||
1428 | int tilesize) | ||
1429 | { | ||
1430 | } | ||
1431 | |||
1432 | #ifdef COMBINED | ||
1433 | #define thegame sokoban | ||
1434 | #endif | ||
1435 | |||
1436 | const struct game thegame = { | ||
1437 | "Sokoban", NULL, NULL, | ||
1438 | default_params, | ||
1439 | game_fetch_preset, NULL, | ||
1440 | decode_params, | ||
1441 | encode_params, | ||
1442 | free_params, | ||
1443 | dup_params, | ||
1444 | true, game_configure, custom_params, | ||
1445 | validate_params, | ||
1446 | new_game_desc, | ||
1447 | validate_desc, | ||
1448 | new_game, | ||
1449 | dup_game, | ||
1450 | free_game, | ||
1451 | false, solve_game, | ||
1452 | false, game_can_format_as_text_now, game_text_format, | ||
1453 | NULL, NULL, /* get_prefs, set_prefs */ | ||
1454 | new_ui, | ||
1455 | free_ui, | ||
1456 | NULL, /* encode_ui */ | ||
1457 | NULL, /* decode_ui */ | ||
1458 | NULL, /* game_request_keys */ | ||
1459 | game_changed_state, | ||
1460 | NULL, /* current_key_label */ | ||
1461 | interpret_move, | ||
1462 | execute_move, | ||
1463 | PREFERRED_TILESIZE, game_compute_size, game_set_size, | ||
1464 | game_colours, | ||
1465 | game_new_drawstate, | ||
1466 | game_free_drawstate, | ||
1467 | game_redraw, | ||
1468 | game_anim_length, | ||
1469 | game_flash_length, | ||
1470 | game_get_cursor_location, | ||
1471 | game_status, | ||
1472 | false, false, game_print_size, game_print, | ||
1473 | false, /* wants_statusbar */ | ||
1474 | false, game_timing_state, | ||
1475 | 0, /* flags */ | ||
1476 | }; | ||