summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/unfinished
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/unfinished')
-rw-r--r--apps/plugins/puzzles/src/unfinished/README9
-rw-r--r--apps/plugins/puzzles/src/unfinished/group.R25
-rw-r--r--apps/plugins/puzzles/src/unfinished/group.c2198
-rw-r--r--apps/plugins/puzzles/src/unfinished/group.gap97
-rw-r--r--apps/plugins/puzzles/src/unfinished/numgame.c1290
-rw-r--r--apps/plugins/puzzles/src/unfinished/path.c786
-rw-r--r--apps/plugins/puzzles/src/unfinished/separate.R21
-rw-r--r--apps/plugins/puzzles/src/unfinished/separate.c859
-rw-r--r--apps/plugins/puzzles/src/unfinished/slide.R24
-rw-r--r--apps/plugins/puzzles/src/unfinished/slide.c2445
-rw-r--r--apps/plugins/puzzles/src/unfinished/sokoban.R19
-rw-r--r--apps/plugins/puzzles/src/unfinished/sokoban.c1479
12 files changed, 9252 insertions, 0 deletions
diff --git a/apps/plugins/puzzles/src/unfinished/README b/apps/plugins/puzzles/src/unfinished/README
new file mode 100644
index 0000000000..0f8bb41d7c
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/README
@@ -0,0 +1,9 @@
1This subdirectory contains puzzle implementations which are
2half-written, fundamentally flawed, or in other ways unready to be
3shipped as part of the polished Puzzles collection.
4
5Those puzzles which have .R files can be built as part of the
6Puzzles collection by symlinking their source files into the parent
7directory and re-running mkfiles.pl. Anything without a .R file
8isn't even finished enough to do that, and you should read the
9source file itself to find out the status.
diff --git a/apps/plugins/puzzles/src/unfinished/group.R b/apps/plugins/puzzles/src/unfinished/group.R
new file mode 100644
index 0000000000..a11d22e9b9
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/group.R
@@ -0,0 +1,25 @@
1# -*- makefile -*-
2
3GROUP_LATIN_EXTRA = tree234 maxflow
4GROUP_EXTRA = latin GROUP_LATIN_EXTRA
5
6group : [X] GTK COMMON group GROUP_EXTRA group-icon|no-icon
7
8group : [G] WINDOWS COMMON group GROUP_EXTRA group.res|noicon.res
9
10groupsolver : [U] group[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] GROUP_LATIN_EXTRA STANDALONE
11groupsolver : [C] group[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] GROUP_LATIN_EXTRA STANDALONE
12
13ALL += group[COMBINED] GROUP_EXTRA
14
15!begin am gtk
16GAMES += group
17!end
18
19!begin >list.c
20 A(group) \
21!end
22
23!begin >gamedesc.txt
24group:group.exe:Group:Group theory puzzle:Complete the unfinished Cayley table of a group.
25!end
diff --git a/apps/plugins/puzzles/src/unfinished/group.c b/apps/plugins/puzzles/src/unfinished/group.c
new file mode 100644
index 0000000000..4a4ad6ce53
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/group.c
@@ -0,0 +1,2198 @@
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#include <math.h>
35
36#include "puzzles.h"
37#include "latin.h"
38
39/*
40 * Difficulty levels. I do some macro ickery here to ensure that my
41 * enum and the various forms of my name list always match up.
42 */
43#define DIFFLIST(A) \
44 A(TRIVIAL,Trivial,NULL,t) \
45 A(NORMAL,Normal,solver_normal,n) \
46 A(HARD,Hard,NULL,h) \
47 A(EXTREME,Extreme,NULL,x) \
48 A(UNREASONABLE,Unreasonable,NULL,u)
49#define ENUM(upper,title,func,lower) DIFF_ ## upper,
50#define TITLE(upper,title,func,lower) #title,
51#define ENCODE(upper,title,func,lower) #lower
52#define CONFIG(upper,title,func,lower) ":" #title
53enum { DIFFLIST(ENUM) DIFFCOUNT };
54static char const *const group_diffnames[] = { DIFFLIST(TITLE) };
55static char const group_diffchars[] = DIFFLIST(ENCODE);
56#define DIFFCONFIG DIFFLIST(CONFIG)
57
58enum {
59 COL_BACKGROUND,
60 COL_GRID,
61 COL_USER,
62 COL_HIGHLIGHT,
63 COL_ERROR,
64 COL_PENCIL,
65 COL_DIAGONAL,
66 NCOLOURS
67};
68
69/*
70 * In identity mode, we number the elements e,a,b,c,d,f,g,h,...
71 * Otherwise, they're a,b,c,d,e,f,g,h,... in the obvious way.
72 */
73#define E_TO_FRONT(c,id) ( (id) && (c)<=5 ? (c) % 5 + 1 : (c) )
74#define E_FROM_FRONT(c,id) ( (id) && (c)<=5 ? ((c) + 3) % 5 + 1 : (c) )
75
76#define FROMCHAR(c,id) E_TO_FRONT((((c)-('A'-1)) & ~0x20), id)
77#define ISCHAR(c) (((c)>='A'&&(c)<='Z') || ((c)>='a'&&(c)<='z'))
78#define TOCHAR(c,id) (E_FROM_FRONT(c,id) + ('a'-1))
79
80struct game_params {
81 int w, diff, id;
82};
83
84struct game_state {
85 game_params par;
86 digit *grid;
87 unsigned char *immutable;
88 int *pencil; /* bitmaps using bits 1<<1..1<<n */
89 int completed, cheated;
90 digit *sequence; /* sequence of group elements shown */
91
92 /*
93 * This array indicates thick lines separating rows and columns
94 * placed and unplaced manually by the user as a visual aid, e.g.
95 * to delineate a subgroup and its cosets.
96 *
97 * When a line is placed, it's deemed to be between the two
98 * particular group elements that are on either side of it at the
99 * time; dragging those two away from each other automatically
100 * gets rid of the line. Hence, for a given element i, dividers[i]
101 * is either -1 (indicating no divider to the right of i), or some
102 * other element (indicating a divider to the right of i iff that
103 * element is the one right of it). These are eagerly cleared
104 * during drags.
105 */
106 int *dividers; /* thick lines between rows/cols */
107};
108
109static game_params *default_params(void)
110{
111 game_params *ret = snew(game_params);
112
113 ret->w = 6;
114 ret->diff = DIFF_NORMAL;
115 ret->id = TRUE;
116
117 return ret;
118}
119
120const static struct game_params group_presets[] = {
121 { 6, DIFF_NORMAL, TRUE },
122 { 6, DIFF_NORMAL, FALSE },
123 { 8, DIFF_NORMAL, TRUE },
124 { 8, DIFF_NORMAL, FALSE },
125 { 8, DIFF_HARD, TRUE },
126 { 8, DIFF_HARD, FALSE },
127 { 12, DIFF_NORMAL, TRUE },
128};
129
130static int game_fetch_preset(int i, char **name, game_params **params)
131{
132 game_params *ret;
133 char buf[80];
134
135 if (i < 0 || i >= lenof(group_presets))
136 return FALSE;
137
138 ret = snew(game_params);
139 *ret = group_presets[i]; /* structure copy */
140
141 sprintf(buf, "%dx%d %s%s", ret->w, ret->w, group_diffnames[ret->diff],
142 ret->id ? "" : ", identity hidden");
143
144 *name = dupstr(buf);
145 *params = ret;
146 return TRUE;
147}
148
149static void free_params(game_params *params)
150{
151 sfree(params);
152}
153
154static game_params *dup_params(const game_params *params)
155{
156 game_params *ret = snew(game_params);
157 *ret = *params; /* structure copy */
158 return ret;
159}
160
161static void decode_params(game_params *params, char const *string)
162{
163 char const *p = string;
164
165 params->w = atoi(p);
166 while (*p && isdigit((unsigned char)*p)) p++;
167 params->diff = DIFF_NORMAL;
168 params->id = TRUE;
169
170 while (*p) {
171 if (*p == 'd') {
172 int i;
173 p++;
174 params->diff = DIFFCOUNT+1; /* ...which is invalid */
175 if (*p) {
176 for (i = 0; i < DIFFCOUNT; i++) {
177 if (*p == group_diffchars[i])
178 params->diff = i;
179 }
180 p++;
181 }
182 } else if (*p == 'i') {
183 params->id = FALSE;
184 p++;
185 } else {
186 /* unrecognised character */
187 p++;
188 }
189 }
190}
191
192static char *encode_params(const game_params *params, int full)
193{
194 char ret[80];
195
196 sprintf(ret, "%d", params->w);
197 if (full)
198 sprintf(ret + strlen(ret), "d%c", group_diffchars[params->diff]);
199 if (!params->id)
200 sprintf(ret + strlen(ret), "i");
201
202 return dupstr(ret);
203}
204
205static config_item *game_configure(const game_params *params)
206{
207 config_item *ret;
208 char buf[80];
209
210 ret = snewn(4, config_item);
211
212 ret[0].name = "Grid size";
213 ret[0].type = C_STRING;
214 sprintf(buf, "%d", params->w);
215 ret[0].sval = dupstr(buf);
216 ret[0].ival = 0;
217
218 ret[1].name = "Difficulty";
219 ret[1].type = C_CHOICES;
220 ret[1].sval = DIFFCONFIG;
221 ret[1].ival = params->diff;
222
223 ret[2].name = "Show identity";
224 ret[2].type = C_BOOLEAN;
225 ret[2].sval = NULL;
226 ret[2].ival = params->id;
227
228 ret[3].name = NULL;
229 ret[3].type = C_END;
230 ret[3].sval = NULL;
231 ret[3].ival = 0;
232
233 return ret;
234}
235
236static game_params *custom_params(const config_item *cfg)
237{
238 game_params *ret = snew(game_params);
239
240 ret->w = atoi(cfg[0].sval);
241 ret->diff = cfg[1].ival;
242 ret->id = cfg[2].ival;
243
244 return ret;
245}
246
247static char *validate_params(const game_params *params, int full)
248{
249 if (params->w < 3 || params->w > 26)
250 return "Grid size must be between 3 and 26";
251 if (params->diff >= DIFFCOUNT)
252 return "Unknown difficulty rating";
253 if (!params->id && params->diff == DIFF_TRIVIAL) {
254 /*
255 * We can't have a Trivial-difficulty puzzle (i.e. latin
256 * square deductions only) without a clear identity, because
257 * identityless puzzles always have two rows and two columns
258 * entirely blank, and no latin-square deduction permits the
259 * distinguishing of two such rows.
260 */
261 return "Trivial puzzles must have an identity";
262 }
263 if (!params->id && params->w == 3) {
264 /*
265 * We can't have a 3x3 puzzle without an identity either,
266 * because 3x3 puzzles can't ever be harder than Trivial
267 * (there are no 3x3 latin squares which aren't also valid
268 * group tables, so enabling group-based deductions doesn't
269 * rule out any possible solutions) and - as above - Trivial
270 * puzzles can't not have an identity.
271 */
272 return "3x3 puzzles must have an identity";
273 }
274 return NULL;
275}
276
277/* ----------------------------------------------------------------------
278 * Solver.
279 */
280
281static int solver_normal(struct latin_solver *solver, void *vctx)
282{
283 int w = solver->o;
284#ifdef STANDALONE_SOLVER
285 char **names = solver->names;
286#endif
287 digit *grid = solver->grid;
288 int i, j, k;
289
290 /*
291 * Deduce using associativity: (ab)c = a(bc).
292 *
293 * So we pick any a,b,c we like; then if we know ab, bc, and
294 * (ab)c we can fill in a(bc).
295 */
296 for (i = 1; i < w; i++)
297 for (j = 1; j < w; j++)
298 for (k = 1; k < w; k++) {
299 if (!grid[i*w+j] || !grid[j*w+k])
300 continue;
301 if (grid[(grid[i*w+j]-1)*w+k] &&
302 !grid[i*w+(grid[j*w+k]-1)]) {
303 int x = grid[j*w+k]-1, y = i;
304 int n = grid[(grid[i*w+j]-1)*w+k];
305#ifdef STANDALONE_SOLVER
306 if (solver_show_working) {
307 printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n",
308 solver_recurse_depth*4, "",
309 names[i], names[j], names[k],
310 names[grid[i*w+j]-1], names[k],
311 names[i], names[grid[j*w+k]-1]);
312 printf("%*s placing %s at (%d,%d)\n",
313 solver_recurse_depth*4, "",
314 names[n-1], x+1, y+1);
315 }
316#endif
317 if (solver->cube[(x*w+y)*w+n-1]) {
318 latin_solver_place(solver, x, y, n);
319 return 1;
320 } else {
321#ifdef STANDALONE_SOLVER
322 if (solver_show_working)
323 printf("%*s contradiction!\n",
324 solver_recurse_depth*4, "");
325 return -1;
326#endif
327 }
328 }
329 if (!grid[(grid[i*w+j]-1)*w+k] &&
330 grid[i*w+(grid[j*w+k]-1)]) {
331 int x = k, y = grid[i*w+j]-1;
332 int n = grid[i*w+(grid[j*w+k]-1)];
333#ifdef STANDALONE_SOLVER
334 if (solver_show_working) {
335 printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n",
336 solver_recurse_depth*4, "",
337 names[i], names[j], names[k],
338 names[grid[i*w+j]-1], names[k],
339 names[i], names[grid[j*w+k]-1]);
340 printf("%*s placing %s at (%d,%d)\n",
341 solver_recurse_depth*4, "",
342 names[n-1], x+1, y+1);
343 }
344#endif
345 if (solver->cube[(x*w+y)*w+n-1]) {
346 latin_solver_place(solver, x, y, n);
347 return 1;
348 } else {
349#ifdef STANDALONE_SOLVER
350 if (solver_show_working)
351 printf("%*s contradiction!\n",
352 solver_recurse_depth*4, "");
353 return -1;
354#endif
355 }
356 }
357 }
358
359 return 0;
360}
361
362#define SOLVER(upper,title,func,lower) func,
363static usersolver_t const group_solvers[] = { DIFFLIST(SOLVER) };
364
365static int solver(const game_params *params, digit *grid, int maxdiff)
366{
367 int w = params->w;
368 int ret;
369 struct latin_solver solver;
370#ifdef STANDALONE_SOLVER
371 char *p, text[100], *names[50];
372 int i;
373#endif
374
375 latin_solver_alloc(&solver, grid, w);
376#ifdef STANDALONE_SOLVER
377 for (i = 0, p = text; i < w; i++) {
378 names[i] = p;
379 *p++ = TOCHAR(i+1, params->id);
380 *p++ = '\0';
381 }
382 solver.names = names;
383#endif
384
385 ret = latin_solver_main(&solver, maxdiff,
386 DIFF_TRIVIAL, DIFF_HARD, DIFF_EXTREME,
387 DIFF_EXTREME, DIFF_UNREASONABLE,
388 group_solvers, NULL, NULL, NULL);
389
390 latin_solver_free(&solver);
391
392 return ret;
393}
394
395/* ----------------------------------------------------------------------
396 * Grid generation.
397 */
398
399static char *encode_grid(char *desc, digit *grid, int area)
400{
401 int run, i;
402 char *p = desc;
403
404 run = 0;
405 for (i = 0; i <= area; i++) {
406 int n = (i < area ? grid[i] : -1);
407
408 if (!n)
409 run++;
410 else {
411 if (run) {
412 while (run > 0) {
413 int c = 'a' - 1 + run;
414 if (run > 26)
415 c = 'z';
416 *p++ = c;
417 run -= c - ('a' - 1);
418 }
419 } else {
420 /*
421 * If there's a number in the very top left or
422 * bottom right, there's no point putting an
423 * unnecessary _ before or after it.
424 */
425 if (p > desc && n > 0)
426 *p++ = '_';
427 }
428 if (n > 0)
429 p += sprintf(p, "%d", n);
430 run = 0;
431 }
432 }
433 return p;
434}
435
436/* ----- data generated by group.gap begins ----- */
437
438struct group {
439 unsigned long autosize;
440 int order, ngens;
441 const char *gens;
442};
443struct groups {
444 int ngroups;
445 const struct group *groups;
446};
447
448static const struct group groupdata[] = {
449 /* order 2 */
450 {1L, 2, 1, "BA"},
451 /* order 3 */
452 {2L, 3, 1, "BCA"},
453 /* order 4 */
454 {2L, 4, 1, "BCDA"},
455 {6L, 4, 2, "BADC" "CDAB"},
456 /* order 5 */
457 {4L, 5, 1, "BCDEA"},
458 /* order 6 */
459 {6L, 6, 2, "CFEBAD" "BADCFE"},
460 {2L, 6, 1, "DCFEBA"},
461 /* order 7 */
462 {6L, 7, 1, "BCDEFGA"},
463 /* order 8 */
464 {4L, 8, 1, "BCEFDGHA"},
465 {8L, 8, 2, "BDEFGAHC" "EGBHDCFA"},
466 {8L, 8, 2, "EGBHDCFA" "BAEFCDHG"},
467 {24L, 8, 2, "BDEFGAHC" "CHDGBEAF"},
468 {168L, 8, 3, "BAEFCDHG" "CEAGBHDF" "DFGAHBCE"},
469 /* order 9 */
470 {6L, 9, 1, "BDECGHFIA"},
471 {48L, 9, 2, "BDEAGHCIF" "CEFGHAIBD"},
472 /* order 10 */
473 {20L, 10, 2, "CJEBGDIFAH" "BADCFEHGJI"},
474 {4L, 10, 1, "DCFEHGJIBA"},
475 /* order 11 */
476 {10L, 11, 1, "BCDEFGHIJKA"},
477 /* order 12 */
478 {12L, 12, 2, "GLDKJEHCBIAF" "BCEFAGIJDKLH"},
479 {4L, 12, 1, "EHIJKCBLDGFA"},
480 {24L, 12, 2, "BEFGAIJKCDLH" "FJBKHLEGDCIA"},
481 {12L, 12, 2, "GLDKJEHCBIAF" "BAEFCDIJGHLK"},
482 {12L, 12, 2, "FDIJGHLBKAEC" "GIDKFLHCJEAB"},
483 /* order 13 */
484 {12L, 13, 1, "BCDEFGHIJKLMA"},
485 /* order 14 */
486 {42L, 14, 2, "ELGNIBKDMFAHCJ" "BADCFEHGJILKNM"},
487 {6L, 14, 1, "FEHGJILKNMBADC"},
488 /* order 15 */
489 {8L, 15, 1, "EGHCJKFMNIOBLDA"},
490 /* order 16 */
491 {8L, 16, 1, "MKNPFOADBGLCIEHJ"},
492 {96L, 16, 2, "ILKCONFPEDJHGMAB" "BDFGHIAKLMNCOEPJ"},
493 {32L, 16, 2, "MIHPFDCONBLAKJGE" "BEFGHJKALMNOCDPI"},
494 {32L, 16, 2, "IFACOGLMDEJBNPKH" "BEFGHJKALMNOCDPI"},
495 {16L, 16, 2, "MOHPFKCINBLADJGE" "BDFGHIEKLMNJOAPC"},
496 {16L, 16, 2, "MIHPFDJONBLEKCGA" "BDFGHIEKLMNJOAPC"},
497 {32L, 16, 2, "MOHPFDCINBLEKJGA" "BAFGHCDELMNIJKPO"},
498 {16L, 16, 2, "MIHPFKJONBLADCGE" "GDPHNOEKFLBCIAMJ"},
499 {32L, 16, 2, "MIBPFDJOGHLEKCNA" "CLEIJGMPKAOHNFDB"},
500 {192L, 16, 3,
501 "MCHPFAIJNBLDEOGK" "BEFGHJKALMNOCDPI" "GKLBNOEDFPHJIAMC"},
502 {64L, 16, 3, "MCHPFAIJNBLDEOGK" "LOGFPKJIBNMEDCHA" "CMAIJHPFDEONBLKG"},
503 {192L, 16, 3,
504 "IPKCOGMLEDJBNFAH" "BEFGHJKALMNOCDPI" "CMEIJBPFKAOGHLDN"},
505 {48L, 16, 3, "IPDJONFLEKCBGMAH" "FJBLMEOCGHPKAIND" "DGIEKLHNJOAMPBCF"},
506 {20160L, 16, 4,
507 "EHJKAMNBOCDPFGIL" "BAFGHCDELMNIJKPO" "CFAIJBLMDEOGHPKN"
508 "DGIAKLBNCOEFPHJM"},
509 /* order 17 */
510 {16L, 17, 1, "EFGHIJKLMNOPQABCD"},
511 /* order 18 */
512 {54L, 18, 2, "MKIQOPNAGLRECDBJHF" "BAEFCDJKLGHIOPMNRQ"},
513 {6L, 18, 1, "ECJKGHFOPDMNLRIQBA"},
514 {12L, 18, 2, "ECJKGHBOPAMNFRDQLI" "KNOPQCFREIGHLJAMBD"},
515 {432L, 18, 3,
516 "IFNAKLQCDOPBGHREMJ" "NOQCFRIGHKLJAMPBDE" "BAEFCDJKLGHIOPMNRQ"},
517 {48L, 18, 2, "ECJKGHBOPAMNFRDQLI" "FDKLHIOPBMNAREQCJG"},
518 /* order 19 */
519 {18L, 19, 1, "EFGHIJKLMNOPQRSABCD"},
520 /* order 20 */
521 {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "EABICDFMGHJQKLNTOPRS"},
522 {8L, 20, 1, "EHIJLCMNPGQRSKBTDOFA"},
523 {20L, 20, 2, "DJSHQNCLTRGPEBKAIFOM" "EABICDFMGHJQKLNTOPRS"},
524 {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "ECBIAGFMDKJQHONTLSRP"},
525 {24L, 20, 2, "IGFMDKJQHONTLSREPCBA" "FDIJGHMNKLQROPTBSAEC"},
526 /* order 21 */
527 {42L, 21, 2, "ITLSBOUERDHAGKCJNFMQP" "EJHLMKOPNRSQAUTCDBFGI"},
528 {12L, 21, 1, "EGHCJKFMNIPQLSTOUBRDA"},
529 /* order 22 */
530 {110L, 22, 2, "ETGVIBKDMFOHQJSLUNAPCR" "BADCFEHGJILKNMPORQTSVU"},
531 {10L, 22, 1, "FEHGJILKNMPORQTSVUBADC"},
532 /* order 23 */
533 {22L, 23, 1, "EFGHIJKLMNOPQRSTUVWABCD"},
534 /* order 24 */
535 {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "HRNOPSWCTUVBLDIJXFGAKQME"},
536 {8L, 24, 1, "MQBTUDRWFGHXJELINOPKSAVC"},
537 {24L, 24, 2, "IOQRBEUVFWGHKLAXMNPSCDTJ" "NJXOVGDKSMTFIPQELCURBWAH"},
538 {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "HSNOPWLDTUVBRIAKXFGCQEMJ"},
539 {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "TWHNXLRIOPUMSACQVBFDEJGK"},
540 {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "BAFGHCDEMNOPIJKLTUVQRSXW"},
541 {48L, 24, 3,
542 "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO"
543 "HSNOPWLDTUVBRIAKXFGCQEMJ"},
544 {24L, 24, 3,
545 "QUKJWPXFESRIVBMNLDCGHTAO" "JXEQRVUMKLWCPGFTSAIBONDH"
546 "TRONXLWCHVUMSAIJPGFDEQBK"},
547 {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "VKXHOQASNTPBCWDEUFGIJLMR"},
548 {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "RMLWIGTUSDJQOPFXEKCBVNAH"},
549 {48L, 24, 2, "IULQRGXMSDCWOPNTEKJBVFAH" "GLMOPRSDTUBVWIEKFXHJQANC"},
550 {24L, 24, 2, "UJPXMRCSNHGTLWIKFVBEDQOA" "NRUFVLWIPXMOJEDQHGTCSABK"},
551 {24L, 24, 2, "MIBTUAQRFGHXCDEWNOPJKLVS" "OKXVFWSCGUTNDRQJBPMALIHE"},
552 {144L, 24, 3,
553 "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO"
554 "BAFGHCDEMNOPIJKLTUVQRSXW"},
555 {336L, 24, 3,
556 "QTKJWONXESRIHVUMLDCPGFAB" "JNEQRHTUKLWCOPXFSAIVBMDG"
557 "HENOPJKLTUVBQRSAXFGWCDMI"},
558 /* order 25 */
559 {20L, 25, 1, "EHILMNPQRSFTUVBJWXDOYGAKC"},
560 {480L, 25, 2, "EHILMNPQRSCTUVBFWXDJYGOKA" "BDEGHIKLMNAPQRSCTUVFWXJYO"},
561 /* order 26 */
562 {156L, 26, 2,
563 "EXGZIBKDMFOHQJSLUNWPYRATCV" "BADCFEHGJILKNMPORQTSVUXWZY"},
564 {12L, 26, 1, "FEHGJILKNMPORQTSVUXWZYBADC"},
565};
566
567static const struct groups groups[] = {
568 {0, NULL}, /* trivial case: 0 */
569 {0, NULL}, /* trivial case: 1 */
570 {1, groupdata + 0}, /* 2 */
571 {1, groupdata + 1}, /* 3 */
572 {2, groupdata + 2}, /* 4 */
573 {1, groupdata + 4}, /* 5 */
574 {2, groupdata + 5}, /* 6 */
575 {1, groupdata + 7}, /* 7 */
576 {5, groupdata + 8}, /* 8 */
577 {2, groupdata + 13}, /* 9 */
578 {2, groupdata + 15}, /* 10 */
579 {1, groupdata + 17}, /* 11 */
580 {5, groupdata + 18}, /* 12 */
581 {1, groupdata + 23}, /* 13 */
582 {2, groupdata + 24}, /* 14 */
583 {1, groupdata + 26}, /* 15 */
584 {14, groupdata + 27}, /* 16 */
585 {1, groupdata + 41}, /* 17 */
586 {5, groupdata + 42}, /* 18 */
587 {1, groupdata + 47}, /* 19 */
588 {5, groupdata + 48}, /* 20 */
589 {2, groupdata + 53}, /* 21 */
590 {2, groupdata + 55}, /* 22 */
591 {1, groupdata + 57}, /* 23 */
592 {15, groupdata + 58}, /* 24 */
593 {2, groupdata + 73}, /* 25 */
594 {2, groupdata + 75}, /* 26 */
595};
596
597/* ----- data generated by group.gap ends ----- */
598
599static char *new_game_desc(const game_params *params, random_state *rs,
600 char **aux, int interactive)
601{
602 int w = params->w, a = w*w;
603 digit *grid, *soln, *soln2;
604 int *indices;
605 int i, j, k, qh, qt;
606 int diff = params->diff;
607 const struct group *group;
608 char *desc, *p;
609
610 /*
611 * Difficulty exceptions: some combinations of size and
612 * difficulty cannot be satisfied, because all puzzles of at
613 * most that difficulty are actually even easier.
614 *
615 * Remember to re-test this whenever a change is made to the
616 * solver logic!
617 *
618 * I tested it using the following shell command:
619
620for d in t n h x u; do
621 for id in '' i; do
622 for i in {3..9}; do
623 echo -n "./group --generate 1 ${i}d${d}${id}: "
624 perl -e 'alarm 30; exec @ARGV' \
625 ./group --generate 1 ${i}d${d}${id} >/dev/null && echo ok
626 done
627 done
628done
629
630 * Of course, it's better to do that after taking the exceptions
631 * _out_, so as to detect exceptions that should be removed as
632 * well as those which should be added.
633 */
634 if (w < 5 && diff == DIFF_UNREASONABLE)
635 diff--;
636 if ((w < 5 || ((w == 6 || w == 8) && params->id)) && diff == DIFF_EXTREME)
637 diff--;
638 if ((w < 6 || (w == 6 && params->id)) && diff == DIFF_HARD)
639 diff--;
640 if ((w < 4 || (w == 4 && params->id)) && diff == DIFF_NORMAL)
641 diff--;
642
643 grid = snewn(a, digit);
644 soln = snewn(a, digit);
645 soln2 = snewn(a, digit);
646 indices = snewn(a, int);
647
648 while (1) {
649 /*
650 * Construct a valid group table, by picking a group from
651 * the above data table, decompressing it into a full
652 * representation by BFS, and then randomly permuting its
653 * non-identity elements.
654 *
655 * We build the canonical table in 'soln' (and use 'grid' as
656 * our BFS queue), then transfer the table into 'grid'
657 * having shuffled the rows.
658 */
659 assert(w >= 2);
660 assert(w < lenof(groups));
661 group = groups[w].groups + random_upto(rs, groups[w].ngroups);
662 assert(group->order == w);
663 memset(soln, 0, a);
664 for (i = 0; i < w; i++)
665 soln[i] = i+1;
666 qh = qt = 0;
667 grid[qt++] = 1;
668 while (qh < qt) {
669 digit *row, *newrow;
670
671 i = grid[qh++];
672 row = soln + (i-1)*w;
673
674 for (j = 0; j < group->ngens; j++) {
675 int nri;
676 const char *gen = group->gens + j*w;
677
678 /*
679 * Apply each group generator to row, constructing a
680 * new row.
681 */
682 nri = gen[row[0]-1] - 'A' + 1; /* which row is it? */
683 newrow = soln + (nri-1)*w;
684 if (!newrow[0]) { /* not done yet */
685 for (k = 0; k < w; k++)
686 newrow[k] = gen[row[k]-1] - 'A' + 1;
687 grid[qt++] = nri;
688 }
689 }
690 }
691 /* That's got the canonical table. Now shuffle it. */
692 for (i = 0; i < w; i++)
693 soln2[i] = i;
694 if (params->id) /* do we shuffle in the identity? */
695 shuffle(soln2+1, w-1, sizeof(*soln2), rs);
696 else
697 shuffle(soln2, w, sizeof(*soln2), rs);
698 for (i = 0; i < w; i++)
699 for (j = 0; j < w; j++)
700 grid[(soln2[i])*w+(soln2[j])] = soln2[soln[i*w+j]-1]+1;
701
702 /*
703 * Remove entries one by one while the puzzle is still
704 * soluble at the appropriate difficulty level.
705 */
706 memcpy(soln, grid, a);
707 if (!params->id) {
708 /*
709 * Start by blanking the entire identity row and column,
710 * and also another row and column so that the player
711 * can't trivially determine which element is the
712 * identity.
713 */
714
715 j = 1 + random_upto(rs, w-1); /* pick a second row/col to blank */
716 for (i = 0; i < w; i++) {
717 grid[(soln2[0])*w+i] = grid[i*w+(soln2[0])] = 0;
718 grid[(soln2[j])*w+i] = grid[i*w+(soln2[j])] = 0;
719 }
720
721 memcpy(soln2, grid, a);
722 if (solver(params, soln2, diff) > diff)
723 continue; /* go round again if that didn't work */
724 }
725
726 k = 0;
727 for (i = (params->id ? 1 : 0); i < w; i++)
728 for (j = (params->id ? 1 : 0); j < w; j++)
729 if (grid[i*w+j])
730 indices[k++] = i*w+j;
731 shuffle(indices, k, sizeof(*indices), rs);
732
733 for (i = 0; i < k; i++) {
734 memcpy(soln2, grid, a);
735 soln2[indices[i]] = 0;
736 if (solver(params, soln2, diff) <= diff)
737 grid[indices[i]] = 0;
738 }
739
740 /*
741 * Make sure the puzzle isn't too easy.
742 */
743 if (diff > 0) {
744 memcpy(soln2, grid, a);
745 if (solver(params, soln2, diff-1) < diff)
746 continue; /* go round and try again */
747 }
748
749 /*
750 * Done.
751 */
752 break;
753 }
754
755 /*
756 * Encode the puzzle description.
757 */
758 desc = snewn(a*20, char);
759 p = encode_grid(desc, grid, a);
760 *p++ = '\0';
761 desc = sresize(desc, p - desc, char);
762
763 /*
764 * Encode the solution.
765 */
766 *aux = snewn(a+2, char);
767 (*aux)[0] = 'S';
768 for (i = 0; i < a; i++)
769 (*aux)[i+1] = TOCHAR(soln[i], params->id);
770 (*aux)[a+1] = '\0';
771
772 sfree(grid);
773 sfree(soln);
774 sfree(soln2);
775 sfree(indices);
776
777 return desc;
778}
779
780/* ----------------------------------------------------------------------
781 * Gameplay.
782 */
783
784static char *validate_grid_desc(const char **pdesc, int range, int area)
785{
786 const char *desc = *pdesc;
787 int squares = 0;
788 while (*desc && *desc != ',') {
789 int n = *desc++;
790 if (n >= 'a' && n <= 'z') {
791 squares += n - 'a' + 1;
792 } else if (n == '_') {
793 /* do nothing */;
794 } else if (n > '0' && n <= '9') {
795 int val = atoi(desc-1);
796 if (val < 1 || val > range)
797 return "Out-of-range number in game description";
798 squares++;
799 while (*desc >= '0' && *desc <= '9')
800 desc++;
801 } else
802 return "Invalid character in game description";
803 }
804
805 if (squares < area)
806 return "Not enough data to fill grid";
807
808 if (squares > area)
809 return "Too much data to fit in grid";
810 *pdesc = desc;
811 return NULL;
812}
813
814static char *validate_desc(const game_params *params, const char *desc)
815{
816 int w = params->w, a = w*w;
817 const char *p = desc;
818
819 return validate_grid_desc(&p, w, a);
820}
821
822static const char *spec_to_grid(const char *desc, digit *grid, int area)
823{
824 int i = 0;
825 while (*desc && *desc != ',') {
826 int n = *desc++;
827 if (n >= 'a' && n <= 'z') {
828 int run = n - 'a' + 1;
829 assert(i + run <= area);
830 while (run-- > 0)
831 grid[i++] = 0;
832 } else if (n == '_') {
833 /* do nothing */;
834 } else if (n > '0' && n <= '9') {
835 assert(i < area);
836 grid[i++] = atoi(desc-1);
837 while (*desc >= '0' && *desc <= '9')
838 desc++;
839 } else {
840 assert(!"We can't get here");
841 }
842 }
843 assert(i == area);
844 return desc;
845}
846
847static game_state *new_game(midend *me, const game_params *params,
848 const char *desc)
849{
850 int w = params->w, a = w*w;
851 game_state *state = snew(game_state);
852 int i;
853
854 state->par = *params; /* structure copy */
855 state->grid = snewn(a, digit);
856 state->immutable = snewn(a, unsigned char);
857 state->pencil = snewn(a, int);
858 for (i = 0; i < a; i++) {
859 state->grid[i] = 0;
860 state->immutable[i] = 0;
861 state->pencil[i] = 0;
862 }
863 state->sequence = snewn(w, digit);
864 state->dividers = snewn(w, int);
865 for (i = 0; i < w; i++) {
866 state->sequence[i] = i;
867 state->dividers[i] = -1;
868 }
869
870 desc = spec_to_grid(desc, state->grid, a);
871 for (i = 0; i < a; i++)
872 if (state->grid[i] != 0)
873 state->immutable[i] = TRUE;
874
875 state->completed = state->cheated = FALSE;
876
877 return state;
878}
879
880static game_state *dup_game(const game_state *state)
881{
882 int w = state->par.w, a = w*w;
883 game_state *ret = snew(game_state);
884
885 ret->par = state->par; /* structure copy */
886
887 ret->grid = snewn(a, digit);
888 ret->immutable = snewn(a, unsigned char);
889 ret->pencil = snewn(a, int);
890 ret->sequence = snewn(w, digit);
891 ret->dividers = snewn(w, int);
892 memcpy(ret->grid, state->grid, a*sizeof(digit));
893 memcpy(ret->immutable, state->immutable, a*sizeof(unsigned char));
894 memcpy(ret->pencil, state->pencil, a*sizeof(int));
895 memcpy(ret->sequence, state->sequence, w*sizeof(digit));
896 memcpy(ret->dividers, state->dividers, w*sizeof(int));
897
898 ret->completed = state->completed;
899 ret->cheated = state->cheated;
900
901 return ret;
902}
903
904static void free_game(game_state *state)
905{
906 sfree(state->grid);
907 sfree(state->immutable);
908 sfree(state->pencil);
909 sfree(state->sequence);
910 sfree(state);
911}
912
913static char *solve_game(const game_state *state, const game_state *currstate,
914 const char *aux, char **error)
915{
916 int w = state->par.w, a = w*w;
917 int i, ret;
918 digit *soln;
919 char *out;
920
921 if (aux)
922 return dupstr(aux);
923
924 soln = snewn(a, digit);
925 memcpy(soln, state->grid, a*sizeof(digit));
926
927 ret = solver(&state->par, soln, DIFFCOUNT-1);
928
929 if (ret == diff_impossible) {
930 *error = "No solution exists for this puzzle";
931 out = NULL;
932 } else if (ret == diff_ambiguous) {
933 *error = "Multiple solutions exist for this puzzle";
934 out = NULL;
935 } else {
936 out = snewn(a+2, char);
937 out[0] = 'S';
938 for (i = 0; i < a; i++)
939 out[i+1] = TOCHAR(soln[i], state->par.id);
940 out[a+1] = '\0';
941 }
942
943 sfree(soln);
944 return out;
945}
946
947static int game_can_format_as_text_now(const game_params *params)
948{
949 return TRUE;
950}
951
952static char *game_text_format(const game_state *state)
953{
954 int w = state->par.w;
955 int x, y;
956 char *ret, *p, ch;
957
958 ret = snewn(2*w*w+1, char); /* leave room for terminating NUL */
959
960 p = ret;
961 for (y = 0; y < w; y++) {
962 for (x = 0; x < w; x++) {
963 digit d = state->grid[y*w+x];
964
965 if (d == 0) {
966 ch = '.';
967 } else {
968 ch = TOCHAR(d, state->par.id);
969 }
970
971 *p++ = ch;
972 if (x == w-1) {
973 *p++ = '\n';
974 } else {
975 *p++ = ' ';
976 }
977 }
978 }
979
980 assert(p - ret == 2*w*w);
981 *p = '\0';
982 return ret;
983}
984
985struct game_ui {
986 /*
987 * These are the coordinates of the primary highlighted square on
988 * the grid, if hshow = 1.
989 */
990 int hx, hy;
991 /*
992 * These are the coordinates hx,hy _before_ they go through
993 * state->sequence.
994 */
995 int ohx, ohy;
996 /*
997 * These variables give the length and displacement of a diagonal
998 * sequence of highlighted squares starting at ohx,ohy (still if
999 * hshow = 1). To find the squares' real coordinates, for 0<=i<dn,
1000 * compute ohx+i*odx and ohy+i*ody and then map through
1001 * state->sequence.
1002 */
1003 int odx, ody, odn;
1004 /*
1005 * This indicates whether the current highlight is a
1006 * pencil-mark one or a real one.
1007 */
1008 int hpencil;
1009 /*
1010 * This indicates whether or not we're showing the highlight
1011 * (used to be hx = hy = -1); important so that when we're
1012 * using the cursor keys it doesn't keep coming back at a
1013 * fixed position. When hshow = 1, pressing a valid number
1014 * or letter key or Space will enter that number or letter in the grid.
1015 */
1016 int hshow;
1017 /*
1018 * This indicates whether we're using the highlight as a cursor;
1019 * it means that it doesn't vanish on a keypress, and that it is
1020 * allowed on immutable squares.
1021 */
1022 int hcursor;
1023 /*
1024 * This indicates whether we're dragging a table header to
1025 * reposition an entire row or column.
1026 */
1027 int drag; /* 0=none 1=row 2=col */
1028 int dragnum; /* element being dragged */
1029 int dragpos; /* its current position */
1030 int edgepos;
1031};
1032
1033static game_ui *new_ui(const game_state *state)
1034{
1035 game_ui *ui = snew(game_ui);
1036
1037 ui->hx = ui->hy = 0;
1038 ui->hpencil = ui->hshow = ui->hcursor = 0;
1039 ui->drag = 0;
1040
1041 return ui;
1042}
1043
1044static void free_ui(game_ui *ui)
1045{
1046 sfree(ui);
1047}
1048
1049static char *encode_ui(const game_ui *ui)
1050{
1051 return NULL;
1052}
1053
1054static void decode_ui(game_ui *ui, const char *encoding)
1055{
1056}
1057
1058static void game_changed_state(game_ui *ui, const game_state *oldstate,
1059 const game_state *newstate)
1060{
1061 int w = newstate->par.w;
1062 /*
1063 * We prevent pencil-mode highlighting of a filled square, unless
1064 * we're using the cursor keys. So if the user has just filled in
1065 * a square which we had a pencil-mode highlight in (by Undo, or
1066 * by Redo, or by Solve), then we cancel the highlight.
1067 */
1068 if (ui->hshow && ui->hpencil && !ui->hcursor &&
1069 newstate->grid[ui->hy * w + ui->hx] != 0) {
1070 ui->hshow = 0;
1071 }
1072 if (ui->hshow && ui->odn > 1) {
1073 /*
1074 * Reordering of rows or columns within the range of a
1075 * multifill selection cancels the multifill and deselects
1076 * everything.
1077 */
1078 int i;
1079 for (i = 0; i < ui->odn; i++) {
1080 if (oldstate->sequence[ui->ohx + i*ui->odx] !=
1081 newstate->sequence[ui->ohx + i*ui->odx]) {
1082 ui->hshow = 0;
1083 break;
1084 }
1085 if (oldstate->sequence[ui->ohy + i*ui->ody] !=
1086 newstate->sequence[ui->ohy + i*ui->ody]) {
1087 ui->hshow = 0;
1088 break;
1089 }
1090 }
1091 } else if (ui->hshow &&
1092 (newstate->sequence[ui->ohx] != ui->hx ||
1093 newstate->sequence[ui->ohy] != ui->hy)) {
1094 /*
1095 * Otherwise, reordering of the row or column containing the
1096 * selection causes the selection to move with it.
1097 */
1098 int i;
1099 for (i = 0; i < w; i++) {
1100 if (newstate->sequence[i] == ui->hx)
1101 ui->ohx = i;
1102 if (newstate->sequence[i] == ui->hy)
1103 ui->ohy = i;
1104 }
1105 }
1106}
1107
1108#define PREFERRED_TILESIZE 48
1109#define TILESIZE (ds->tilesize)
1110#define BORDER (TILESIZE / 2)
1111#define LEGEND (TILESIZE)
1112#define GRIDEXTRA max((TILESIZE / 32),1)
1113#define COORD(x) ((x)*TILESIZE + BORDER + LEGEND)
1114#define FROMCOORD(x) (((x)+(TILESIZE-BORDER-LEGEND)) / TILESIZE - 1)
1115
1116#define FLASH_TIME 0.4F
1117
1118#define DF_DIVIDER_TOP 0x1000
1119#define DF_DIVIDER_BOT 0x2000
1120#define DF_DIVIDER_LEFT 0x4000
1121#define DF_DIVIDER_RIGHT 0x8000
1122#define DF_HIGHLIGHT 0x0400
1123#define DF_HIGHLIGHT_PENCIL 0x0200
1124#define DF_IMMUTABLE 0x0100
1125#define DF_LEGEND 0x0080
1126#define DF_DIGIT_MASK 0x001F
1127
1128#define EF_DIGIT_SHIFT 5
1129#define EF_DIGIT_MASK ((1 << EF_DIGIT_SHIFT) - 1)
1130#define EF_LEFT_SHIFT 0
1131#define EF_RIGHT_SHIFT (3*EF_DIGIT_SHIFT)
1132#define EF_LEFT_MASK ((1UL << (3*EF_DIGIT_SHIFT)) - 1UL)
1133#define EF_RIGHT_MASK (EF_LEFT_MASK << EF_RIGHT_SHIFT)
1134#define EF_LATIN (1UL << (6*EF_DIGIT_SHIFT))
1135
1136struct game_drawstate {
1137 game_params par;
1138 int w, tilesize;
1139 int started;
1140 long *tiles, *legend, *pencil, *errors;
1141 long *errtmp;
1142 digit *sequence;
1143};
1144
1145static int check_errors(const game_state *state, long *errors)
1146{
1147 int w = state->par.w, a = w*w;
1148 digit *grid = state->grid;
1149 int i, j, k, x, y, errs = FALSE;
1150
1151 /*
1152 * To verify that we have a valid group table, it suffices to
1153 * test latin-square-hood and associativity only. All the other
1154 * group axioms follow from those two.
1155 *
1156 * Proof:
1157 *
1158 * Associativity is given; closure is obvious from latin-
1159 * square-hood. We need to show that an identity exists and that
1160 * every element has an inverse.
1161 *
1162 * Identity: take any element a. There will be some element e
1163 * such that ea=a (in a latin square, every element occurs in
1164 * every row and column, so a must occur somewhere in the a
1165 * column, say on row e). For any other element b, there must
1166 * exist x such that ax=b (same argument from latin-square-hood
1167 * again), and then associativity gives us eb = e(ax) = (ea)x =
1168 * ax = b. Hence eb=b for all b, i.e. e is a left-identity. A
1169 * similar argument tells us that there must be some f which is
1170 * a right-identity, and then we show they are the same element
1171 * by observing that ef must simultaneously equal e and equal f.
1172 *
1173 * Inverses: given any a, by the latin-square argument again,
1174 * there must exist p and q such that pa=e and aq=e (i.e. left-
1175 * and right-inverses). We can show these are equal by
1176 * associativity: p = pe = p(aq) = (pa)q = eq = q. []
1177 */
1178
1179 if (errors)
1180 for (i = 0; i < a; i++)
1181 errors[i] = 0;
1182
1183 for (y = 0; y < w; y++) {
1184 unsigned long mask = 0, errmask = 0;
1185 for (x = 0; x < w; x++) {
1186 unsigned long bit = 1UL << grid[y*w+x];
1187 errmask |= (mask & bit);
1188 mask |= bit;
1189 }
1190
1191 if (mask != (1 << (w+1)) - (1 << 1)) {
1192 errs = TRUE;
1193 errmask &= ~1UL;
1194 if (errors) {
1195 for (x = 0; x < w; x++)
1196 if (errmask & (1UL << grid[y*w+x]))
1197 errors[y*w+x] |= EF_LATIN;
1198 }
1199 }
1200 }
1201
1202 for (x = 0; x < w; x++) {
1203 unsigned long mask = 0, errmask = 0;
1204 for (y = 0; y < w; y++) {
1205 unsigned long bit = 1UL << grid[y*w+x];
1206 errmask |= (mask & bit);
1207 mask |= bit;
1208 }
1209
1210 if (mask != (1 << (w+1)) - (1 << 1)) {
1211 errs = TRUE;
1212 errmask &= ~1UL;
1213 if (errors) {
1214 for (y = 0; y < w; y++)
1215 if (errmask & (1UL << grid[y*w+x]))
1216 errors[y*w+x] |= EF_LATIN;
1217 }
1218 }
1219 }
1220
1221 for (i = 1; i < w; i++)
1222 for (j = 1; j < w; j++)
1223 for (k = 1; k < w; k++)
1224 if (grid[i*w+j] && grid[j*w+k] &&
1225 grid[(grid[i*w+j]-1)*w+k] &&
1226 grid[i*w+(grid[j*w+k]-1)] &&
1227 grid[(grid[i*w+j]-1)*w+k] != grid[i*w+(grid[j*w+k]-1)]) {
1228 if (errors) {
1229 int a = i+1, b = j+1, c = k+1;
1230 int ab = grid[i*w+j], bc = grid[j*w+k];
1231 int left = (ab-1)*w+(c-1), right = (a-1)*w+(bc-1);
1232 /*
1233 * If the appropriate error slot is already
1234 * used for one of the squares, we don't
1235 * fill either of them.
1236 */
1237 if (!(errors[left] & EF_LEFT_MASK) &&
1238 !(errors[right] & EF_RIGHT_MASK)) {
1239 long err;
1240 err = a;
1241 err = (err << EF_DIGIT_SHIFT) | b;
1242 err = (err << EF_DIGIT_SHIFT) | c;
1243 errors[left] |= err << EF_LEFT_SHIFT;
1244 errors[right] |= err << EF_RIGHT_SHIFT;
1245 }
1246 }
1247 errs = TRUE;
1248 }
1249
1250 return errs;
1251}
1252
1253static int find_in_sequence(digit *seq, int len, digit n)
1254{
1255 int i;
1256
1257 for (i = 0; i < len; i++)
1258 if (seq[i] == n)
1259 return i;
1260
1261 assert(!"Should never get here");
1262 return -1;
1263}
1264
1265static char *interpret_move(const game_state *state, game_ui *ui,
1266 const game_drawstate *ds,
1267 int x, int y, int button)
1268{
1269 int w = state->par.w;
1270 int tx, ty;
1271 char buf[80];
1272
1273 button &= ~MOD_MASK;
1274
1275 tx = FROMCOORD(x);
1276 ty = FROMCOORD(y);
1277
1278 if (ui->drag) {
1279 if (IS_MOUSE_DRAG(button)) {
1280 int tcoord = ((ui->drag &~ 4) == 1 ? ty : tx);
1281 ui->drag |= 4; /* some movement has happened */
1282 if (tcoord >= 0 && tcoord < w) {
1283 ui->dragpos = tcoord;
1284 return "";
1285 }
1286 } else if (IS_MOUSE_RELEASE(button)) {
1287 if (ui->drag & 4) {
1288 ui->drag = 0; /* end drag */
1289 if (state->sequence[ui->dragpos] == ui->dragnum)
1290 return ""; /* drag was a no-op overall */
1291 sprintf(buf, "D%d,%d", ui->dragnum, ui->dragpos);
1292 return dupstr(buf);
1293 } else {
1294 ui->drag = 0; /* end 'drag' */
1295 if (ui->edgepos > 0 && ui->edgepos < w) {
1296 sprintf(buf, "V%d,%d",
1297 state->sequence[ui->edgepos-1],
1298 state->sequence[ui->edgepos]);
1299 return dupstr(buf);
1300 } else
1301 return ""; /* no-op */
1302 }
1303 }
1304 } else if (IS_MOUSE_DOWN(button)) {
1305 if (tx >= 0 && tx < w && ty >= 0 && ty < w) {
1306 int otx = tx, oty = ty;
1307 tx = state->sequence[tx];
1308 ty = state->sequence[ty];
1309 if (button == LEFT_BUTTON) {
1310 if (tx == ui->hx && ty == ui->hy &&
1311 ui->hshow && ui->hpencil == 0) {
1312 ui->hshow = 0;
1313 } else {
1314 ui->hx = tx;
1315 ui->hy = ty;
1316 ui->ohx = otx;
1317 ui->ohy = oty;
1318 ui->odx = ui->ody = 0;
1319 ui->odn = 1;
1320 ui->hshow = !state->immutable[ty*w+tx];
1321 ui->hpencil = 0;
1322 }
1323 ui->hcursor = 0;
1324 return ""; /* UI activity occurred */
1325 }
1326 if (button == RIGHT_BUTTON) {
1327 /*
1328 * Pencil-mode highlighting for non filled squares.
1329 */
1330 if (state->grid[ty*w+tx] == 0) {
1331 if (tx == ui->hx && ty == ui->hy &&
1332 ui->hshow && ui->hpencil) {
1333 ui->hshow = 0;
1334 } else {
1335 ui->hpencil = 1;
1336 ui->hx = tx;
1337 ui->hy = ty;
1338 ui->ohx = otx;
1339 ui->ohy = oty;
1340 ui->odx = ui->ody = 0;
1341 ui->odn = 1;
1342 ui->hshow = 1;
1343 }
1344 } else {
1345 ui->hshow = 0;
1346 }
1347 ui->hcursor = 0;
1348 return ""; /* UI activity occurred */
1349 }
1350 } else if (tx >= 0 && tx < w && ty == -1) {
1351 ui->drag = 2;
1352 ui->dragnum = state->sequence[tx];
1353 ui->dragpos = tx;
1354 ui->edgepos = FROMCOORD(x + TILESIZE/2);
1355 return "";
1356 } else if (ty >= 0 && ty < w && tx == -1) {
1357 ui->drag = 1;
1358 ui->dragnum = state->sequence[ty];
1359 ui->dragpos = ty;
1360 ui->edgepos = FROMCOORD(y + TILESIZE/2);
1361 return "";
1362 }
1363 } else if (IS_MOUSE_DRAG(button)) {
1364 if (!ui->hpencil &&
1365 tx >= 0 && tx < w && ty >= 0 && ty < w &&
1366 abs(tx - ui->ohx) == abs(ty - ui->ohy)) {
1367 ui->odn = abs(tx - ui->ohx) + 1;
1368 ui->odx = (tx < ui->ohx ? -1 : +1);
1369 ui->ody = (ty < ui->ohy ? -1 : +1);
1370 } else {
1371 ui->odx = ui->ody = 0;
1372 ui->odn = 1;
1373 }
1374 return "";
1375 }
1376
1377 if (IS_CURSOR_MOVE(button)) {
1378 int cx = find_in_sequence(state->sequence, w, ui->hx);
1379 int cy = find_in_sequence(state->sequence, w, ui->hy);
1380 move_cursor(button, &cx, &cy, w, w, 0);
1381 ui->hx = state->sequence[cx];
1382 ui->hy = state->sequence[cy];
1383 ui->hshow = ui->hcursor = 1;
1384 return "";
1385 }
1386 if (ui->hshow &&
1387 (button == CURSOR_SELECT)) {
1388 ui->hpencil = 1 - ui->hpencil;
1389 ui->hcursor = 1;
1390 return "";
1391 }
1392
1393 if (ui->hshow &&
1394 ((ISCHAR(button) && FROMCHAR(button, state->par.id) <= w) ||
1395 button == CURSOR_SELECT2 || button == '\b')) {
1396 int n = FROMCHAR(button, state->par.id);
1397 int i, buflen;
1398 char *movebuf;
1399
1400 if (button == CURSOR_SELECT2 || button == '\b')
1401 n = 0;
1402
1403 for (i = 0; i < ui->odn; i++) {
1404 int x = state->sequence[ui->ohx + i*ui->odx];
1405 int y = state->sequence[ui->ohy + i*ui->ody];
1406 int index = y*w+x;
1407
1408 /*
1409 * Can't make pencil marks in a filled square. This can only
1410 * become highlighted if we're using cursor keys.
1411 */
1412 if (ui->hpencil && state->grid[index])
1413 return NULL;
1414
1415 /*
1416 * Can't do anything to an immutable square. Exception:
1417 * trying to set it to what it already was is OK (so that
1418 * multifilling can set a whole diagonal to a without
1419 * having to detour round the one immutable square in the
1420 * middle that already said a).
1421 */
1422 if (!ui->hpencil && state->grid[index] == n)
1423 /* OK even if it is immutable */;
1424 else if (state->immutable[index])
1425 return NULL;
1426 }
1427
1428 movebuf = snewn(80 * ui->odn, char);
1429 buflen = sprintf(movebuf, "%c%d,%d,%d",
1430 (char)(ui->hpencil && n > 0 ? 'P' : 'R'),
1431 ui->hx, ui->hy, n);
1432 for (i = 1; i < ui->odn; i++) {
1433 assert(buflen < i*80);
1434 buflen += sprintf(movebuf + buflen, "+%d,%d",
1435 state->sequence[ui->ohx + i*ui->odx],
1436 state->sequence[ui->ohy + i*ui->ody]);
1437 }
1438 movebuf = sresize(movebuf, buflen+1, char);
1439
1440 if (!ui->hcursor) ui->hshow = 0;
1441
1442 return movebuf;
1443 }
1444
1445 if (button == 'M' || button == 'm')
1446 return dupstr("M");
1447
1448 return NULL;
1449}
1450
1451static game_state *execute_move(const game_state *from, const char *move)
1452{
1453 int w = from->par.w, a = w*w;
1454 game_state *ret;
1455 int x, y, i, j, n, pos;
1456
1457 if (move[0] == 'S') {
1458 ret = dup_game(from);
1459 ret->completed = ret->cheated = TRUE;
1460
1461 for (i = 0; i < a; i++) {
1462 if (!ISCHAR(move[i+1]) || FROMCHAR(move[i+1], from->par.id) > w) {
1463 free_game(ret);
1464 return NULL;
1465 }
1466 ret->grid[i] = FROMCHAR(move[i+1], from->par.id);
1467 ret->pencil[i] = 0;
1468 }
1469
1470 if (move[a+1] != '\0') {
1471 free_game(ret);
1472 return NULL;
1473 }
1474
1475 return ret;
1476 } else if ((move[0] == 'P' || move[0] == 'R') &&
1477 sscanf(move+1, "%d,%d,%d%n", &x, &y, &n, &pos) == 3 &&
1478 n >= 0 && n <= w) {
1479 const char *mp = move + 1 + pos;
1480 int pencil = (move[0] == 'P');
1481 ret = dup_game(from);
1482
1483 while (1) {
1484 if (x < 0 || x >= w || y < 0 || y >= w) {
1485 free_game(ret);
1486 return NULL;
1487 }
1488 if (from->immutable[y*w+x] && !(!pencil && from->grid[y*w+x] == n))
1489 return NULL;
1490
1491 if (move[0] == 'P' && n > 0) {
1492 ret->pencil[y*w+x] ^= 1 << n;
1493 } else {
1494 ret->grid[y*w+x] = n;
1495 ret->pencil[y*w+x] = 0;
1496 }
1497
1498 if (!*mp)
1499 break;
1500
1501 if (*mp != '+')
1502 return NULL;
1503 if (sscanf(mp, "+%d,%d%n", &x, &y, &pos) < 2)
1504 return NULL;
1505 mp += pos;
1506 }
1507
1508 if (!ret->completed && !check_errors(ret, NULL))
1509 ret->completed = TRUE;
1510
1511 return ret;
1512 } else if (move[0] == 'M') {
1513 /*
1514 * Fill in absolutely all pencil marks everywhere. (I
1515 * wouldn't use this for actual play, but it's a handy
1516 * starting point when following through a set of
1517 * diagnostics output by the standalone solver.)
1518 */
1519 ret = dup_game(from);
1520 for (i = 0; i < a; i++) {
1521 if (!ret->grid[i])
1522 ret->pencil[i] = (1 << (w+1)) - (1 << 1);
1523 }
1524 return ret;
1525 } else if (move[0] == 'D' &&
1526 sscanf(move+1, "%d,%d", &x, &y) == 2) {
1527 /*
1528 * Reorder the rows and columns so that digit x is in position
1529 * y.
1530 */
1531 ret = dup_game(from);
1532 for (i = j = 0; i < w; i++) {
1533 if (i == y) {
1534 ret->sequence[i] = x;
1535 } else {
1536 if (from->sequence[j] == x)
1537 j++;
1538 ret->sequence[i] = from->sequence[j++];
1539 }
1540 }
1541 /*
1542 * Eliminate any obsoleted dividers.
1543 */
1544 for (x = 0; x < w; x++) {
1545 int i = ret->sequence[x];
1546 int j = (x+1 < w ? ret->sequence[x+1] : -1);
1547 if (ret->dividers[i] != j)
1548 ret->dividers[i] = -1;
1549 }
1550 return ret;
1551 } else if (move[0] == 'V' &&
1552 sscanf(move+1, "%d,%d", &i, &j) == 2) {
1553 ret = dup_game(from);
1554 if (ret->dividers[i] == j)
1555 ret->dividers[i] = -1;
1556 else
1557 ret->dividers[i] = j;
1558 return ret;
1559 } else
1560 return NULL; /* couldn't parse move string */
1561}
1562
1563/* ----------------------------------------------------------------------
1564 * Drawing routines.
1565 */
1566
1567#define SIZE(w) ((w) * TILESIZE + 2*BORDER + LEGEND)
1568
1569static void game_compute_size(const game_params *params, int tilesize,
1570 int *x, int *y)
1571{
1572 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1573 struct { int tilesize; } ads, *ds = &ads;
1574 ads.tilesize = tilesize;
1575
1576 *x = *y = SIZE(params->w);
1577}
1578
1579static void game_set_size(drawing *dr, game_drawstate *ds,
1580 const game_params *params, int tilesize)
1581{
1582 ds->tilesize = tilesize;
1583}
1584
1585static float *game_colours(frontend *fe, int *ncolours)
1586{
1587 float *ret = snewn(3 * NCOLOURS, float);
1588
1589 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1590
1591 ret[COL_GRID * 3 + 0] = 0.0F;
1592 ret[COL_GRID * 3 + 1] = 0.0F;
1593 ret[COL_GRID * 3 + 2] = 0.0F;
1594
1595 ret[COL_USER * 3 + 0] = 0.0F;
1596 ret[COL_USER * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1];
1597 ret[COL_USER * 3 + 2] = 0.0F;
1598
1599 ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0];
1600 ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1];
1601 ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2];
1602
1603 ret[COL_ERROR * 3 + 0] = 1.0F;
1604 ret[COL_ERROR * 3 + 1] = 0.0F;
1605 ret[COL_ERROR * 3 + 2] = 0.0F;
1606
1607 ret[COL_PENCIL * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
1608 ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
1609 ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
1610
1611 ret[COL_DIAGONAL * 3 + 0] = 0.95F * ret[COL_BACKGROUND * 3 + 0];
1612 ret[COL_DIAGONAL * 3 + 1] = 0.95F * ret[COL_BACKGROUND * 3 + 1];
1613 ret[COL_DIAGONAL * 3 + 2] = 0.95F * ret[COL_BACKGROUND * 3 + 2];
1614
1615 *ncolours = NCOLOURS;
1616 return ret;
1617}
1618
1619static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1620{
1621 int w = state->par.w, a = w*w;
1622 struct game_drawstate *ds = snew(struct game_drawstate);
1623 int i;
1624
1625 ds->w = w;
1626 ds->par = state->par; /* structure copy */
1627 ds->tilesize = 0;
1628 ds->started = FALSE;
1629 ds->tiles = snewn(a, long);
1630 ds->legend = snewn(w, long);
1631 ds->pencil = snewn(a, long);
1632 ds->errors = snewn(a, long);
1633 ds->sequence = snewn(a, digit);
1634 for (i = 0; i < a; i++)
1635 ds->tiles[i] = ds->pencil[i] = -1;
1636 for (i = 0; i < w; i++)
1637 ds->legend[i] = -1;
1638 ds->errtmp = snewn(a, long);
1639
1640 return ds;
1641}
1642
1643static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1644{
1645 sfree(ds->tiles);
1646 sfree(ds->pencil);
1647 sfree(ds->errors);
1648 sfree(ds->errtmp);
1649 sfree(ds->sequence);
1650 sfree(ds);
1651}
1652
1653static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, long tile,
1654 long pencil, long error)
1655{
1656 int w = ds->w /* , a = w*w */;
1657 int tx, ty, tw, th;
1658 int cx, cy, cw, ch;
1659 char str[64];
1660
1661 tx = BORDER + LEGEND + x * TILESIZE + 1;
1662 ty = BORDER + LEGEND + y * TILESIZE + 1;
1663
1664 cx = tx;
1665 cy = ty;
1666 cw = tw = TILESIZE-1;
1667 ch = th = TILESIZE-1;
1668
1669 if (tile & DF_LEGEND) {
1670 cx += TILESIZE/10;
1671 cy += TILESIZE/10;
1672 cw -= TILESIZE/5;
1673 ch -= TILESIZE/5;
1674 tile |= DF_IMMUTABLE;
1675 }
1676
1677 clip(dr, cx, cy, cw, ch);
1678
1679 /* background needs erasing */
1680 draw_rect(dr, cx, cy, cw, ch,
1681 (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT :
1682 (x == y) ? COL_DIAGONAL : COL_BACKGROUND);
1683
1684 /* dividers */
1685 if (tile & DF_DIVIDER_TOP)
1686 draw_rect(dr, cx, cy, cw, 1, COL_GRID);
1687 if (tile & DF_DIVIDER_BOT)
1688 draw_rect(dr, cx, cy+ch-1, cw, 1, COL_GRID);
1689 if (tile & DF_DIVIDER_LEFT)
1690 draw_rect(dr, cx, cy, 1, ch, COL_GRID);
1691 if (tile & DF_DIVIDER_RIGHT)
1692 draw_rect(dr, cx+cw-1, cy, 1, ch, COL_GRID);
1693
1694 /* pencil-mode highlight */
1695 if (tile & DF_HIGHLIGHT_PENCIL) {
1696 int coords[6];
1697 coords[0] = cx;
1698 coords[1] = cy;
1699 coords[2] = cx+cw/2;
1700 coords[3] = cy;
1701 coords[4] = cx;
1702 coords[5] = cy+ch/2;
1703 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
1704 }
1705
1706 /* new number needs drawing? */
1707 if (tile & DF_DIGIT_MASK) {
1708 str[1] = '\0';
1709 str[0] = TOCHAR(tile & DF_DIGIT_MASK, ds->par.id);
1710 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1711 FONT_VARIABLE, TILESIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE,
1712 (error & EF_LATIN) ? COL_ERROR :
1713 (tile & DF_IMMUTABLE) ? COL_GRID : COL_USER, str);
1714
1715 if (error & EF_LEFT_MASK) {
1716 int a = (error >> (EF_LEFT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
1717 int b = (error >> (EF_LEFT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
1718 int c = (error >> (EF_LEFT_SHIFT ))&EF_DIGIT_MASK;
1719 char buf[10];
1720 sprintf(buf, "(%c%c)%c", TOCHAR(a, ds->par.id),
1721 TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id));
1722 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/6,
1723 FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE,
1724 COL_ERROR, buf);
1725 }
1726 if (error & EF_RIGHT_MASK) {
1727 int a = (error >> (EF_RIGHT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
1728 int b = (error >> (EF_RIGHT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
1729 int c = (error >> (EF_RIGHT_SHIFT ))&EF_DIGIT_MASK;
1730 char buf[10];
1731 sprintf(buf, "%c(%c%c)", TOCHAR(a, ds->par.id),
1732 TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id));
1733 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE - TILESIZE/6,
1734 FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE,
1735 COL_ERROR, buf);
1736 }
1737 } else {
1738 int i, j, npencil;
1739 int pl, pr, pt, pb;
1740 float bestsize;
1741 int pw, ph, minph, pbest, fontsize;
1742
1743 /* Count the pencil marks required. */
1744 for (i = 1, npencil = 0; i <= w; i++)
1745 if (pencil & (1 << i))
1746 npencil++;
1747 if (npencil) {
1748
1749 minph = 2;
1750
1751 /*
1752 * Determine the bounding rectangle within which we're going
1753 * to put the pencil marks.
1754 */
1755 /* Start with the whole square */
1756 pl = tx + GRIDEXTRA;
1757 pr = pl + TILESIZE - GRIDEXTRA;
1758 pt = ty + GRIDEXTRA;
1759 pb = pt + TILESIZE - GRIDEXTRA;
1760
1761 /*
1762 * We arrange our pencil marks in a grid layout, with
1763 * the number of rows and columns adjusted to allow the
1764 * maximum font size.
1765 *
1766 * So now we work out what the grid size ought to be.
1767 */
1768 bestsize = 0.0;
1769 pbest = 0;
1770 /* Minimum */
1771 for (pw = 3; pw < max(npencil,4); pw++) {
1772 float fw, fh, fs;
1773
1774 ph = (npencil + pw - 1) / pw;
1775 ph = max(ph, minph);
1776 fw = (pr - pl) / (float)pw;
1777 fh = (pb - pt) / (float)ph;
1778 fs = min(fw, fh);
1779 if (fs > bestsize) {
1780 bestsize = fs;
1781 pbest = pw;
1782 }
1783 }
1784 assert(pbest > 0);
1785 pw = pbest;
1786 ph = (npencil + pw - 1) / pw;
1787 ph = max(ph, minph);
1788
1789 /*
1790 * Now we've got our grid dimensions, work out the pixel
1791 * size of a grid element, and round it to the nearest
1792 * pixel. (We don't want rounding errors to make the
1793 * grid look uneven at low pixel sizes.)
1794 */
1795 fontsize = min((pr - pl) / pw, (pb - pt) / ph);
1796
1797 /*
1798 * Centre the resulting figure in the square.
1799 */
1800 pl = tx + (TILESIZE - fontsize * pw) / 2;
1801 pt = ty + (TILESIZE - fontsize * ph) / 2;
1802
1803 /*
1804 * Now actually draw the pencil marks.
1805 */
1806 for (i = 1, j = 0; i <= w; i++)
1807 if (pencil & (1 << i)) {
1808 int dx = j % pw, dy = j / pw;
1809
1810 str[1] = '\0';
1811 str[0] = TOCHAR(i, ds->par.id);
1812 draw_text(dr, pl + fontsize * (2*dx+1) / 2,
1813 pt + fontsize * (2*dy+1) / 2,
1814 FONT_VARIABLE, fontsize,
1815 ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str);
1816 j++;
1817 }
1818 }
1819 }
1820
1821 unclip(dr);
1822
1823 draw_update(dr, cx, cy, cw, ch);
1824}
1825
1826static void game_redraw(drawing *dr, game_drawstate *ds,
1827 const game_state *oldstate, const game_state *state,
1828 int dir, const game_ui *ui,
1829 float animtime, float flashtime)
1830{
1831 int w = state->par.w /*, a = w*w */;
1832 int x, y, i, j;
1833
1834 if (!ds->started) {
1835 /*
1836 * The initial contents of the window are not guaranteed and
1837 * can vary with front ends. To be on the safe side, all
1838 * games should start by drawing a big background-colour
1839 * rectangle covering the whole window.
1840 */
1841 draw_rect(dr, 0, 0, SIZE(w), SIZE(w), COL_BACKGROUND);
1842
1843 /*
1844 * Big containing rectangle.
1845 */
1846 draw_rect(dr, COORD(0) - GRIDEXTRA, COORD(0) - GRIDEXTRA,
1847 w*TILESIZE+1+GRIDEXTRA*2, w*TILESIZE+1+GRIDEXTRA*2,
1848 COL_GRID);
1849
1850 draw_update(dr, 0, 0, SIZE(w), SIZE(w));
1851
1852 ds->started = TRUE;
1853 }
1854
1855 check_errors(state, ds->errtmp);
1856
1857 /*
1858 * Construct a modified version of state->sequence which takes
1859 * into account an unfinished drag operation.
1860 */
1861 if (ui->drag) {
1862 x = ui->dragnum;
1863 y = ui->dragpos;
1864 } else {
1865 x = y = -1;
1866 }
1867 for (i = j = 0; i < w; i++) {
1868 if (i == y) {
1869 ds->sequence[i] = x;
1870 } else {
1871 if (state->sequence[j] == x)
1872 j++;
1873 ds->sequence[i] = state->sequence[j++];
1874 }
1875 }
1876
1877 /*
1878 * Draw the table legend.
1879 */
1880 for (x = 0; x < w; x++) {
1881 int sx = ds->sequence[x];
1882 long tile = (sx+1) | DF_LEGEND;
1883 if (ds->legend[x] != tile) {
1884 ds->legend[x] = tile;
1885 draw_tile(dr, ds, -1, x, tile, 0, 0);
1886 draw_tile(dr, ds, x, -1, tile, 0, 0);
1887 }
1888 }
1889
1890 for (y = 0; y < w; y++) {
1891 int sy = ds->sequence[y];
1892 for (x = 0; x < w; x++) {
1893 long tile = 0L, pencil = 0L, error;
1894 int sx = ds->sequence[x];
1895
1896 if (state->grid[sy*w+sx])
1897 tile = state->grid[sy*w+sx];
1898 else
1899 pencil = (long)state->pencil[sy*w+sx];
1900
1901 if (state->immutable[sy*w+sx])
1902 tile |= DF_IMMUTABLE;
1903
1904 if ((ui->drag == 5 && ui->dragnum == sy) ||
1905 (ui->drag == 6 && ui->dragnum == sx)) {
1906 tile |= DF_HIGHLIGHT;
1907 } else if (ui->hshow) {
1908 int i = abs(x - ui->ohx);
1909 int highlight = 0;
1910 if (ui->odn > 1) {
1911 /*
1912 * When a diagonal multifill selection is shown,
1913 * we show it in its original grid position
1914 * regardless of in-progress row/col drags. Moving
1915 * every square about would be horrible.
1916 */
1917 if (i >= 0 && i < ui->odn &&
1918 x == ui->ohx + i*ui->odx &&
1919 y == ui->ohy + i*ui->ody)
1920 highlight = 1;
1921 } else {
1922 /*
1923 * For a single square, we move its highlight
1924 * around with the drag.
1925 */
1926 highlight = (ui->hx == sx && ui->hy == sy);
1927 }
1928 if (highlight)
1929 tile |= (ui->hpencil ? DF_HIGHLIGHT_PENCIL : DF_HIGHLIGHT);
1930 }
1931
1932 if (flashtime > 0 &&
1933 (flashtime <= FLASH_TIME/3 ||
1934 flashtime >= FLASH_TIME*2/3))
1935 tile |= DF_HIGHLIGHT; /* completion flash */
1936
1937 if (y <= 0 || state->dividers[ds->sequence[y-1]] == sy)
1938 tile |= DF_DIVIDER_TOP;
1939 if (y+1 >= w || state->dividers[sy] == ds->sequence[y+1])
1940 tile |= DF_DIVIDER_BOT;
1941 if (x <= 0 || state->dividers[ds->sequence[x-1]] == sx)
1942 tile |= DF_DIVIDER_LEFT;
1943 if (x+1 >= w || state->dividers[sx] == ds->sequence[x+1])
1944 tile |= DF_DIVIDER_RIGHT;
1945
1946 error = ds->errtmp[sy*w+sx];
1947
1948 if (ds->tiles[y*w+x] != tile ||
1949 ds->pencil[y*w+x] != pencil ||
1950 ds->errors[y*w+x] != error) {
1951 ds->tiles[y*w+x] = tile;
1952 ds->pencil[y*w+x] = pencil;
1953 ds->errors[y*w+x] = error;
1954 draw_tile(dr, ds, x, y, tile, pencil, error);
1955 }
1956 }
1957 }
1958}
1959
1960static float game_anim_length(const game_state *oldstate,
1961 const game_state *newstate, int dir, game_ui *ui)
1962{
1963 return 0.0F;
1964}
1965
1966static float game_flash_length(const game_state *oldstate,
1967 const game_state *newstate, int dir, game_ui *ui)
1968{
1969 if (!oldstate->completed && newstate->completed &&
1970 !oldstate->cheated && !newstate->cheated)
1971 return FLASH_TIME;
1972 return 0.0F;
1973}
1974
1975static int game_status(const game_state *state)
1976{
1977 return state->completed ? +1 : 0;
1978}
1979
1980static int game_timing_state(const game_state *state, game_ui *ui)
1981{
1982 if (state->completed)
1983 return FALSE;
1984 return TRUE;
1985}
1986
1987static void game_print_size(const game_params *params, float *x, float *y)
1988{
1989 int pw, ph;
1990
1991 /*
1992 * We use 9mm squares by default, like Solo.
1993 */
1994 game_compute_size(params, 900, &pw, &ph);
1995 *x = pw / 100.0F;
1996 *y = ph / 100.0F;
1997}
1998
1999static void game_print(drawing *dr, const game_state *state, int tilesize)
2000{
2001 int w = state->par.w;
2002 int ink = print_mono_colour(dr, 0);
2003 int x, y;
2004
2005 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2006 game_drawstate ads, *ds = &ads;
2007 game_set_size(dr, ds, NULL, tilesize);
2008
2009 /*
2010 * Border.
2011 */
2012 print_line_width(dr, 3 * TILESIZE / 40);
2013 draw_rect_outline(dr, BORDER + LEGEND, BORDER + LEGEND,
2014 w*TILESIZE, w*TILESIZE, ink);
2015
2016 /*
2017 * Legend on table.
2018 */
2019 for (x = 0; x < w; x++) {
2020 char str[2];
2021 str[1] = '\0';
2022 str[0] = TOCHAR(x+1, state->par.id);
2023 draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2,
2024 BORDER + TILESIZE/2,
2025 FONT_VARIABLE, TILESIZE/2,
2026 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
2027 draw_text(dr, BORDER + TILESIZE/2,
2028 BORDER+LEGEND + x*TILESIZE + TILESIZE/2,
2029 FONT_VARIABLE, TILESIZE/2,
2030 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
2031 }
2032
2033 /*
2034 * Main grid.
2035 */
2036 for (x = 1; x < w; x++) {
2037 print_line_width(dr, TILESIZE / 40);
2038 draw_line(dr, BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND,
2039 BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND+w*TILESIZE, ink);
2040 }
2041 for (y = 1; y < w; y++) {
2042 print_line_width(dr, TILESIZE / 40);
2043 draw_line(dr, BORDER+LEGEND, BORDER+LEGEND+y*TILESIZE,
2044 BORDER+LEGEND+w*TILESIZE, BORDER+LEGEND+y*TILESIZE, ink);
2045 }
2046
2047 /*
2048 * Numbers.
2049 */
2050 for (y = 0; y < w; y++)
2051 for (x = 0; x < w; x++)
2052 if (state->grid[y*w+x]) {
2053 char str[2];
2054 str[1] = '\0';
2055 str[0] = TOCHAR(state->grid[y*w+x], state->par.id);
2056 draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2,
2057 BORDER+LEGEND + y*TILESIZE + TILESIZE/2,
2058 FONT_VARIABLE, TILESIZE/2,
2059 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
2060 }
2061}
2062
2063#ifdef COMBINED
2064#define thegame group
2065#endif
2066
2067const struct game thegame = {
2068 "Group", NULL, NULL,
2069 default_params,
2070 game_fetch_preset, NULL,
2071 decode_params,
2072 encode_params,
2073 free_params,
2074 dup_params,
2075 TRUE, game_configure, custom_params,
2076 validate_params,
2077 new_game_desc,
2078 validate_desc,
2079 new_game,
2080 dup_game,
2081 free_game,
2082 TRUE, solve_game,
2083 TRUE, game_can_format_as_text_now, game_text_format,
2084 new_ui,
2085 free_ui,
2086 encode_ui,
2087 decode_ui,
2088 game_changed_state,
2089 interpret_move,
2090 execute_move,
2091 PREFERRED_TILESIZE, game_compute_size, game_set_size,
2092 game_colours,
2093 game_new_drawstate,
2094 game_free_drawstate,
2095 game_redraw,
2096 game_anim_length,
2097 game_flash_length,
2098 game_status,
2099 TRUE, FALSE, game_print_size, game_print,
2100 FALSE, /* wants_statusbar */
2101 FALSE, game_timing_state,
2102 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
2103};
2104
2105#ifdef STANDALONE_SOLVER
2106
2107#include <stdarg.h>
2108
2109int main(int argc, char **argv)
2110{
2111 game_params *p;
2112 game_state *s;
2113 char *id = NULL, *desc, *err;
2114 digit *grid;
2115 int grade = FALSE;
2116 int ret, diff, really_show_working = FALSE;
2117
2118 while (--argc > 0) {
2119 char *p = *++argv;
2120 if (!strcmp(p, "-v")) {
2121 really_show_working = TRUE;
2122 } else if (!strcmp(p, "-g")) {
2123 grade = TRUE;
2124 } else if (*p == '-') {
2125 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2126 return 1;
2127 } else {
2128 id = p;
2129 }
2130 }
2131
2132 if (!id) {
2133 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
2134 return 1;
2135 }
2136
2137 desc = strchr(id, ':');
2138 if (!desc) {
2139 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2140 return 1;
2141 }
2142 *desc++ = '\0';
2143
2144 p = default_params();
2145 decode_params(p, id);
2146 err = validate_desc(p, desc);
2147 if (err) {
2148 fprintf(stderr, "%s: %s\n", argv[0], err);
2149 return 1;
2150 }
2151 s = new_game(NULL, p, desc);
2152
2153 grid = snewn(p->w * p->w, digit);
2154
2155 /*
2156 * When solving a Normal puzzle, we don't want to bother the
2157 * user with Hard-level deductions. For this reason, we grade
2158 * the puzzle internally before doing anything else.
2159 */
2160 ret = -1; /* placate optimiser */
2161 solver_show_working = FALSE;
2162 for (diff = 0; diff < DIFFCOUNT; diff++) {
2163 memcpy(grid, s->grid, p->w * p->w);
2164 ret = solver(&s->par, grid, diff);
2165 if (ret <= diff)
2166 break;
2167 }
2168
2169 if (diff == DIFFCOUNT) {
2170 if (grade)
2171 printf("Difficulty rating: ambiguous\n");
2172 else
2173 printf("Unable to find a unique solution\n");
2174 } else {
2175 if (grade) {
2176 if (ret == diff_impossible)
2177 printf("Difficulty rating: impossible (no solution exists)\n");
2178 else
2179 printf("Difficulty rating: %s\n", group_diffnames[ret]);
2180 } else {
2181 solver_show_working = really_show_working;
2182 memcpy(grid, s->grid, p->w * p->w);
2183 ret = solver(&s->par, grid, diff);
2184 if (ret != diff)
2185 printf("Puzzle is inconsistent\n");
2186 else {
2187 memcpy(s->grid, grid, p->w * p->w);
2188 fputs(game_text_format(s), stdout);
2189 }
2190 }
2191 }
2192
2193 return 0;
2194}
2195
2196#endif
2197
2198/* 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
4Print("/* ----- data generated by group.gap begins ----- */\n\n");
5Print("struct group {\n unsigned long autosize;\n");
6Print(" int order, ngens;\n const char *gens;\n};\n");
7Print("struct groups {\n int ngroups;\n");
8Print(" const struct group *groups;\n};\n\n");
9Print("static const struct group groupdata[] = {\n");
10offsets := [0];
11offset := 0;
12for 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);
87od;
88Print("};\n\nstatic const struct groups groups[] = {\n");
89Print(" {0, NULL}, /* trivial case: 0 */\n");
90Print(" {0, NULL}, /* trivial case: 1 */\n");
91n := 2;
92for i in [1..Size(offsets)-1] do
93 Print(" {", offsets[i+1] - offsets[i], ", groupdata+",
94 offsets[i], "}, /* ", i+1, " */\n");
95od;
96Print("};\n\n/* ----- data generated by group.gap ends ----- */\n");
97quit;
diff --git a/apps/plugins/puzzles/src/unfinished/numgame.c b/apps/plugins/puzzles/src/unfinished/numgame.c
new file mode 100644
index 0000000000..aed5c17347
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/numgame.c
@@ -0,0 +1,1290 @@
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#include <math.h>
44
45#include "puzzles.h"
46#include "tree234.h"
47
48/*
49 * To search for numbers we can make, we employ a breadth-first
50 * search across the space of sets of input numbers. That is, for
51 * example, we start with the set (3,6,25,50,75,100); we apply
52 * moves which involve combining two numbers (e.g. adding the 50
53 * and the 75 takes us to the set (3,6,25,100,125); and then we see
54 * if we ever end up with a set containing (say) 952.
55 *
56 * If the rules are changed so that all the numbers must be used,
57 * this is easy to adjust to: we simply see if we end up with a set
58 * containing _only_ (say) 952.
59 *
60 * Obviously, we can vary the rules about permitted arithmetic
61 * operations simply by altering the set of valid moves in the bfs.
62 * However, there's one common rule in this sort of puzzle which
63 * takes a little more thought, and that's _concatenation_. For
64 * example, if you are given (say) four 4s and required to make 10,
65 * you are permitted to combine two of the 4s into a 44 to begin
66 * with, making (44-4)/4 = 10. However, you are generally not
67 * allowed to concatenate two numbers that _weren't_ both in the
68 * original input set (you couldn't multiply two 4s to get 16 and
69 * then concatenate a 4 on to it to make 164), so concatenation is
70 * not an operation which is valid in all situations.
71 *
72 * We could enforce this restriction by storing a flag alongside
73 * each number indicating whether or not it's an original number;
74 * the rules being that concatenation of two numbers is only valid
75 * if they both have the original flag, and that its output _also_
76 * has the original flag (so that you can concatenate three 4s into
77 * a 444), but that applying any other arithmetic operation clears
78 * the original flag on the output. However, we can get marginally
79 * simpler than that by observing that since concatenation has to
80 * happen to a number before any other operation, we can simply
81 * place all the concatenations at the start of the search. In
82 * other words, we have a global flag on an entire number _set_
83 * which indicates whether we are still permitted to perform
84 * concatenations; if so, we can concatenate any of the numbers in
85 * that set. Performing any other operation clears the flag.
86 */
87
88#define SETFLAG_CONCAT 1 /* we can do concatenation */
89
90struct sets;
91
92struct ancestor {
93 struct set *prev; /* index of ancestor set in set list */
94 unsigned char pa, pb, po, pr; /* operation that got here from prev */
95};
96
97struct set {
98 int *numbers; /* rationals stored as n,d pairs */
99 short nnumbers; /* # of rationals, so half # of ints */
100 short flags; /* SETFLAG_CONCAT only, at present */
101 int npaths; /* number of ways to reach this set */
102 struct ancestor a; /* primary ancestor */
103 struct ancestor *as; /* further ancestors, if we care */
104 int nas, assize;
105};
106
107struct output {
108 int number;
109 struct set *set;
110 int index; /* which number in the set is it? */
111 int npaths; /* number of ways to reach this */
112};
113
114#define SETLISTLEN 1024
115#define NUMBERLISTLEN 32768
116#define OUTPUTLISTLEN 1024
117struct operation;
118struct sets {
119 struct set **setlists;
120 int nsets, nsetlists, setlistsize;
121 tree234 *settree;
122 int **numberlists;
123 int nnumbers, nnumberlists, numberlistsize;
124 struct output **outputlists;
125 int noutputs, noutputlists, outputlistsize;
126 tree234 *outputtree;
127 const struct operation *const *ops;
128};
129
130#define OPFLAG_NEEDS_CONCAT 1
131#define OPFLAG_KEEPS_CONCAT 2
132#define OPFLAG_UNARY 4
133#define OPFLAG_UNARYPREFIX 8
134#define OPFLAG_FN 16
135
136struct operation {
137 /*
138 * Most operations should be shown in the output working, but
139 * concatenation should not; we just take the result of the
140 * concatenation and assume that it's obvious how it was
141 * derived.
142 */
143 int display;
144
145 /*
146 * Text display of the operator, in expressions and for
147 * debugging respectively.
148 */
149 char *text, *dbgtext;
150
151 /*
152 * Flags dictating when the operator can be applied.
153 */
154 int flags;
155
156 /*
157 * Priority of the operator (for avoiding unnecessary
158 * parentheses when formatting it into a string).
159 */
160 int priority;
161
162 /*
163 * Associativity of the operator. Bit 0 means we need parens
164 * when the left operand of one of these operators is another
165 * instance of it, e.g. (2^3)^4. Bit 1 means we need parens
166 * when the right operand is another instance of the same
167 * operator, e.g. 2-(3-4). Thus:
168 *
169 * - this field is 0 for a fully associative operator, since
170 * we never need parens.
171 * - it's 1 for a right-associative operator.
172 * - it's 2 for a left-associative operator.
173 * - it's 3 for a _non_-associative operator (which always
174 * uses parens just to be sure).
175 */
176 int assoc;
177
178 /*
179 * Whether the operator is commutative. Saves time in the
180 * search if we don't have to try it both ways round.
181 */
182 int commutes;
183
184 /*
185 * Function which implements the operator. Returns TRUE on
186 * success, FALSE on failure. Takes two rationals and writes
187 * out a third.
188 */
189 int (*perform)(int *a, int *b, int *output);
190};
191
192struct rules {
193 const struct operation *const *ops;
194 int use_all;
195};
196
197#define MUL(r, a, b) do { \
198 (r) = (a) * (b); \
199 if ((b) && (a) && (r) / (b) != (a)) return FALSE; \
200} while (0)
201
202#define ADD(r, a, b) do { \
203 (r) = (a) + (b); \
204 if ((a) > 0 && (b) > 0 && (r) < 0) return FALSE; \
205 if ((a) < 0 && (b) < 0 && (r) > 0) return FALSE; \
206} while (0)
207
208#define OUT(output, n, d) do { \
209 int g = gcd((n),(d)); \
210 if (g < 0) g = -g; \
211 if ((d) < 0) g = -g; \
212 if (g == -1 && (n) < -INT_MAX) return FALSE; \
213 if (g == -1 && (d) < -INT_MAX) return FALSE; \
214 (output)[0] = (n)/g; \
215 (output)[1] = (d)/g; \
216 assert((output)[1] > 0); \
217} while (0)
218
219static int gcd(int x, int y)
220{
221 while (x != 0 && y != 0) {
222 int t = x;
223 x = y;
224 y = t % y;
225 }
226
227 return abs(x + y); /* i.e. whichever one isn't zero */
228}
229
230static int perform_add(int *a, int *b, int *output)
231{
232 int at, bt, tn, bn;
233 /*
234 * a0/a1 + b0/b1 = (a0*b1 + b0*a1) / (a1*b1)
235 */
236 MUL(at, a[0], b[1]);
237 MUL(bt, b[0], a[1]);
238 ADD(tn, at, bt);
239 MUL(bn, a[1], b[1]);
240 OUT(output, tn, bn);
241 return TRUE;
242}
243
244static int perform_sub(int *a, int *b, int *output)
245{
246 int at, bt, tn, bn;
247 /*
248 * a0/a1 - b0/b1 = (a0*b1 - b0*a1) / (a1*b1)
249 */
250 MUL(at, a[0], b[1]);
251 MUL(bt, b[0], a[1]);
252 ADD(tn, at, -bt);
253 MUL(bn, a[1], b[1]);
254 OUT(output, tn, bn);
255 return TRUE;
256}
257
258static int perform_mul(int *a, int *b, int *output)
259{
260 int tn, bn;
261 /*
262 * a0/a1 * b0/b1 = (a0*b0) / (a1*b1)
263 */
264 MUL(tn, a[0], b[0]);
265 MUL(bn, a[1], b[1]);
266 OUT(output, tn, bn);
267 return TRUE;
268}
269
270static int perform_div(int *a, int *b, int *output)
271{
272 int tn, bn;
273
274 /*
275 * Division by zero is outlawed.
276 */
277 if (b[0] == 0)
278 return FALSE;
279
280 /*
281 * a0/a1 / b0/b1 = (a0*b1) / (a1*b0)
282 */
283 MUL(tn, a[0], b[1]);
284 MUL(bn, a[1], b[0]);
285 OUT(output, tn, bn);
286 return TRUE;
287}
288
289static int perform_exact_div(int *a, int *b, int *output)
290{
291 int tn, bn;
292
293 /*
294 * Division by zero is outlawed.
295 */
296 if (b[0] == 0)
297 return FALSE;
298
299 /*
300 * a0/a1 / b0/b1 = (a0*b1) / (a1*b0)
301 */
302 MUL(tn, a[0], b[1]);
303 MUL(bn, a[1], b[0]);
304 OUT(output, tn, bn);
305
306 /*
307 * Exact division means we require the result to be an integer.
308 */
309 return (output[1] == 1);
310}
311
312static int max_p10(int n, int *p10_r)
313{
314 /*
315 * Find the smallest power of ten strictly greater than n.
316 *
317 * Special case: we must return at least 10, even if n is
318 * zero. (This is because this function is used for finding
319 * the power of ten by which to multiply a number being
320 * concatenated to the front of n, and concatenating 1 to 0
321 * should yield 10 and not 1.)
322 */
323 int p10 = 10;
324 while (p10 <= (INT_MAX/10) && p10 <= n)
325 p10 *= 10;
326 if (p10 > INT_MAX/10)
327 return FALSE; /* integer overflow */
328 *p10_r = p10;
329 return TRUE;
330}
331
332static int perform_concat(int *a, int *b, int *output)
333{
334 int t1, t2, p10;
335
336 /*
337 * We can't concatenate anything which isn't a non-negative
338 * integer.
339 */
340 if (a[1] != 1 || b[1] != 1 || a[0] < 0 || b[0] < 0)
341 return FALSE;
342
343 /*
344 * For concatenation, we can safely assume leading zeroes
345 * aren't an issue. It isn't clear whether they `should' be
346 * allowed, but it turns out not to matter: concatenating a
347 * leading zero on to a number in order to harmlessly get rid
348 * of the zero is never necessary because unwanted zeroes can
349 * be disposed of by adding them to something instead. So we
350 * disallow them always.
351 *
352 * The only other possibility is that you might want to
353 * concatenate a leading zero on to something and then
354 * concatenate another non-zero digit on to _that_ (to make,
355 * for example, 106); but that's also unnecessary, because you
356 * can make 106 just as easily by concatenating the 0 on to the
357 * _end_ of the 1 first.
358 */
359 if (a[0] == 0)
360 return FALSE;
361
362 if (!max_p10(b[0], &p10)) return FALSE;
363
364 MUL(t1, p10, a[0]);
365 ADD(t2, t1, b[0]);
366 OUT(output, t2, 1);
367 return TRUE;
368}
369
370#define IPOW(ret, x, y) do { \
371 int ipow_limit = (y); \
372 if ((x) == 1 || (x) == 0) ipow_limit = 1; \
373 else if ((x) == -1) ipow_limit &= 1; \
374 (ret) = 1; \
375 while (ipow_limit-- > 0) { \
376 int tmp; \
377 MUL(tmp, ret, x); \
378 ret = tmp; \
379 } \
380} while (0)
381
382static int perform_exp(int *a, int *b, int *output)
383{
384 int an, ad, xn, xd;
385
386 /*
387 * Exponentiation is permitted if the result is rational. This
388 * means that:
389 *
390 * - first we see whether we can take the (denominator-of-b)th
391 * root of a and get a rational; if not, we give up.
392 *
393 * - then we do take that root of a
394 *
395 * - then we multiply by itself (numerator-of-b) times.
396 */
397 if (b[1] > 1) {
398 an = (int)(0.5 + pow(a[0], 1.0/b[1]));
399 ad = (int)(0.5 + pow(a[1], 1.0/b[1]));
400 IPOW(xn, an, b[1]);
401 IPOW(xd, ad, b[1]);
402 if (xn != a[0] || xd != a[1])
403 return FALSE;
404 } else {
405 an = a[0];
406 ad = a[1];
407 }
408 if (b[0] >= 0) {
409 IPOW(xn, an, b[0]);
410 IPOW(xd, ad, b[0]);
411 } else {
412 IPOW(xd, an, -b[0]);
413 IPOW(xn, ad, -b[0]);
414 }
415 if (xd == 0)
416 return FALSE;
417
418 OUT(output, xn, xd);
419 return TRUE;
420}
421
422static int perform_factorial(int *a, int *b, int *output)
423{
424 int ret, t, i;
425
426 /*
427 * Factorials of non-negative integers are permitted.
428 */
429 if (a[1] != 1 || a[0] < 0)
430 return FALSE;
431
432 /*
433 * However, a special case: we don't take a factorial of
434 * anything which would thereby remain the same.
435 */
436 if (a[0] == 1 || a[0] == 2)
437 return FALSE;
438
439 ret = 1;
440 for (i = 1; i <= a[0]; i++) {
441 MUL(t, ret, i);
442 ret = t;
443 }
444
445 OUT(output, ret, 1);
446 return TRUE;
447}
448
449static int perform_decimal(int *a, int *b, int *output)
450{
451 int p10;
452
453 /*
454 * Add a decimal digit to the front of a number;
455 * fail if it's not an integer.
456 * So, 1 --> 0.1, 15 --> 0.15,
457 * or, rather, 1 --> 1/10, 15 --> 15/100,
458 * x --> x / (smallest power of 10 > than x)
459 *
460 */
461 if (a[1] != 1) return FALSE;
462
463 if (!max_p10(a[0], &p10)) return FALSE;
464
465 OUT(output, a[0], p10);
466 return TRUE;
467}
468
469static int perform_recur(int *a, int *b, int *output)
470{
471 int p10, tn, bn;
472
473 /*
474 * This converts a number like .4 to .44444..., or .45 to .45454...
475 * The input number must be -1 < a < 1.
476 *
477 * Calculate the smallest power of 10 that divides the denominator exactly,
478 * returning if no such power of 10 exists. Then multiply the numerator
479 * up accordingly, and the new denominator becomes that power of 10 - 1.
480 */
481 if (abs(a[0]) >= abs(a[1])) return FALSE; /* -1 < a < 1 */
482
483 p10 = 10;
484 while (p10 <= (INT_MAX/10)) {
485 if ((a[1] <= p10) && (p10 % a[1]) == 0) goto found;
486 p10 *= 10;
487 }
488 return FALSE;
489found:
490 tn = a[0] * (p10 / a[1]);
491 bn = p10 - 1;
492
493 OUT(output, tn, bn);
494 return TRUE;
495}
496
497static int perform_root(int *a, int *b, int *output)
498{
499 /*
500 * A root B is: 1 iff a == 0
501 * B ^ (1/A) otherwise
502 */
503 int ainv[2], res;
504
505 if (a[0] == 0) {
506 OUT(output, 1, 1);
507 return TRUE;
508 }
509
510 OUT(ainv, a[1], a[0]);
511 res = perform_exp(b, ainv, output);
512 return res;
513}
514
515static int perform_perc(int *a, int *b, int *output)
516{
517 if (a[0] == 0) return FALSE; /* 0% = 0, uninteresting. */
518 if (a[1] > (INT_MAX/100)) return FALSE;
519
520 OUT(output, a[0], a[1]*100);
521 return TRUE;
522}
523
524static int perform_gamma(int *a, int *b, int *output)
525{
526 int asub1[2];
527
528 /*
529 * gamma(a) = (a-1)!
530 *
531 * special case not caught by perform_fact: gamma(1) is 1 so
532 * don't bother.
533 */
534 if (a[0] == 1 && a[1] == 1) return FALSE;
535
536 OUT(asub1, a[0]-a[1], a[1]);
537 return perform_factorial(asub1, b, output);
538}
539
540static int perform_sqrt(int *a, int *b, int *output)
541{
542 int half[2] = { 1, 2 };
543
544 /*
545 * sqrt(0) == 0, sqrt(1) == 1: don't perform unary noops.
546 */
547 if (a[0] == 0 || (a[0] == 1 && a[1] == 1)) return FALSE;
548
549 return perform_exp(a, half, output);
550}
551
552const static struct operation op_add = {
553 TRUE, "+", "+", 0, 10, 0, TRUE, perform_add
554};
555const static struct operation op_sub = {
556 TRUE, "-", "-", 0, 10, 2, FALSE, perform_sub
557};
558const static struct operation op_mul = {
559 TRUE, "*", "*", 0, 20, 0, TRUE, perform_mul
560};
561const static struct operation op_div = {
562 TRUE, "/", "/", 0, 20, 2, FALSE, perform_div
563};
564const static struct operation op_xdiv = {
565 TRUE, "/", "/", 0, 20, 2, FALSE, perform_exact_div
566};
567const static struct operation op_concat = {
568 FALSE, "", "concat", OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT,
569 1000, 0, FALSE, perform_concat
570};
571const static struct operation op_exp = {
572 TRUE, "^", "^", 0, 30, 1, FALSE, perform_exp
573};
574const static struct operation op_factorial = {
575 TRUE, "!", "!", OPFLAG_UNARY, 40, 0, FALSE, perform_factorial
576};
577const static struct operation op_decimal = {
578 TRUE, ".", ".", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT, 50, 0, FALSE, perform_decimal
579};
580const static struct operation op_recur = {
581 TRUE, "...", "recur", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 2, FALSE, perform_recur
582};
583const static struct operation op_root = {
584 TRUE, "v~", "root", 0, 30, 1, FALSE, perform_root
585};
586const static struct operation op_perc = {
587 TRUE, "%", "%", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 1, FALSE, perform_perc
588};
589const static struct operation op_gamma = {
590 TRUE, "gamma", "gamma", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_FN, 1, 3, FALSE, perform_gamma
591};
592const static struct operation op_sqrt = {
593 TRUE, "v~", "sqrt", OPFLAG_UNARY | OPFLAG_UNARYPREFIX, 30, 1, FALSE, perform_sqrt
594};
595
596/*
597 * In Countdown, divisions resulting in fractions are disallowed.
598 * http://www.askoxford.com/wordgames/countdown/rules/
599 */
600const static struct operation *const ops_countdown[] = {
601 &op_add, &op_mul, &op_sub, &op_xdiv, NULL
602};
603const static struct rules rules_countdown = {
604 ops_countdown, FALSE
605};
606
607/*
608 * A slightly different rule set which handles the reasonably well
609 * known puzzle of making 24 using two 3s and two 8s. For this we
610 * need rational rather than integer division.
611 */
612const static struct operation *const ops_3388[] = {
613 &op_add, &op_mul, &op_sub, &op_div, NULL
614};
615const static struct rules rules_3388 = {
616 ops_3388, TRUE
617};
618
619/*
620 * A still more permissive rule set usable for the four-4s problem
621 * and similar things. Permits concatenation.
622 */
623const static struct operation *const ops_four4s[] = {
624 &op_add, &op_mul, &op_sub, &op_div, &op_concat, NULL
625};
626const static struct rules rules_four4s = {
627 ops_four4s, TRUE
628};
629
630/*
631 * The most permissive ruleset I can think of. Permits
632 * exponentiation, and also silly unary operators like factorials.
633 */
634const static struct operation *const ops_anythinggoes[] = {
635 &op_add, &op_mul, &op_sub, &op_div, &op_concat, &op_exp, &op_factorial,
636 &op_decimal, &op_recur, &op_root, &op_perc, &op_gamma, &op_sqrt, NULL
637};
638const static struct rules rules_anythinggoes = {
639 ops_anythinggoes, TRUE
640};
641
642#define ratcmp(a,op,b) ( (long long)(a)[0] * (b)[1] op \
643 (long long)(b)[0] * (a)[1] )
644
645static int addtoset(struct set *set, int newnumber[2])
646{
647 int i, j;
648
649 /* Find where we want to insert the new number */
650 for (i = 0; i < set->nnumbers &&
651 ratcmp(set->numbers+2*i, <, newnumber); i++);
652
653 /* Move everything else up */
654 for (j = set->nnumbers; j > i; j--) {
655 set->numbers[2*j] = set->numbers[2*j-2];
656 set->numbers[2*j+1] = set->numbers[2*j-1];
657 }
658
659 /* Insert the new number */
660 set->numbers[2*i] = newnumber[0];
661 set->numbers[2*i+1] = newnumber[1];
662
663 set->nnumbers++;
664
665 return i;
666}
667
668#define ensure(array, size, newlen, type) do { \
669 if ((newlen) > (size)) { \
670 (size) = (newlen) + 512; \
671 (array) = sresize((array), (size), type); \
672 } \
673} while (0)
674
675static int setcmp(void *av, void *bv)
676{
677 struct set *a = (struct set *)av;
678 struct set *b = (struct set *)bv;
679 int i;
680
681 if (a->nnumbers < b->nnumbers)
682 return -1;
683 else if (a->nnumbers > b->nnumbers)
684 return +1;
685
686 if (a->flags < b->flags)
687 return -1;
688 else if (a->flags > b->flags)
689 return +1;
690
691 for (i = 0; i < a->nnumbers; i++) {
692 if (ratcmp(a->numbers+2*i, <, b->numbers+2*i))
693 return -1;
694 else if (ratcmp(a->numbers+2*i, >, b->numbers+2*i))
695 return +1;
696 }
697
698 return 0;
699}
700
701static int outputcmp(void *av, void *bv)
702{
703 struct output *a = (struct output *)av;
704 struct output *b = (struct output *)bv;
705
706 if (a->number < b->number)
707 return -1;
708 else if (a->number > b->number)
709 return +1;
710
711 return 0;
712}
713
714static int outputfindcmp(void *av, void *bv)
715{
716 int *a = (int *)av;
717 struct output *b = (struct output *)bv;
718
719 if (*a < b->number)
720 return -1;
721 else if (*a > b->number)
722 return +1;
723
724 return 0;
725}
726
727static void addset(struct sets *s, struct set *set, int multiple,
728 struct set *prev, int pa, int po, int pb, int pr)
729{
730 struct set *s2;
731 int npaths = (prev ? prev->npaths : 1);
732
733 assert(set == s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN);
734 s2 = add234(s->settree, set);
735 if (s2 == set) {
736 /*
737 * New set added to the tree.
738 */
739 set->a.prev = prev;
740 set->a.pa = pa;
741 set->a.po = po;
742 set->a.pb = pb;
743 set->a.pr = pr;
744 set->npaths = npaths;
745 s->nsets++;
746 s->nnumbers += 2 * set->nnumbers;
747 set->as = NULL;
748 set->nas = set->assize = 0;
749 } else {
750 /*
751 * Rediscovered an existing set. Update its npaths.
752 */
753 s2->npaths += npaths;
754 /*
755 * And optionally enter it as an additional ancestor.
756 */
757 if (multiple) {
758 if (s2->nas >= s2->assize) {
759 s2->assize = s2->nas * 3 / 2 + 4;
760 s2->as = sresize(s2->as, s2->assize, struct ancestor);
761 }
762 s2->as[s2->nas].prev = prev;
763 s2->as[s2->nas].pa = pa;
764 s2->as[s2->nas].po = po;
765 s2->as[s2->nas].pb = pb;
766 s2->as[s2->nas].pr = pr;
767 s2->nas++;
768 }
769 }
770}
771
772static struct set *newset(struct sets *s, int nnumbers, int flags)
773{
774 struct set *sn;
775
776 ensure(s->setlists, s->setlistsize, s->nsets/SETLISTLEN+1, struct set *);
777 while (s->nsetlists <= s->nsets / SETLISTLEN)
778 s->setlists[s->nsetlists++] = snewn(SETLISTLEN, struct set);
779 sn = s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN;
780
781 if (s->nnumbers + nnumbers * 2 > s->nnumberlists * NUMBERLISTLEN)
782 s->nnumbers = s->nnumberlists * NUMBERLISTLEN;
783 ensure(s->numberlists, s->numberlistsize,
784 s->nnumbers/NUMBERLISTLEN+1, int *);
785 while (s->nnumberlists <= s->nnumbers / NUMBERLISTLEN)
786 s->numberlists[s->nnumberlists++] = snewn(NUMBERLISTLEN, int);
787 sn->numbers = s->numberlists[s->nnumbers / NUMBERLISTLEN] +
788 s->nnumbers % NUMBERLISTLEN;
789
790 /*
791 * Start the set off empty.
792 */
793 sn->nnumbers = 0;
794
795 sn->flags = flags;
796
797 return sn;
798}
799
800static int addoutput(struct sets *s, struct set *ss, int index, int *n)
801{
802 struct output *o, *o2;
803
804 /*
805 * Target numbers are always integers.
806 */
807 if (ss->numbers[2*index+1] != 1)
808 return FALSE;
809
810 ensure(s->outputlists, s->outputlistsize, s->noutputs/OUTPUTLISTLEN+1,
811 struct output *);
812 while (s->noutputlists <= s->noutputs / OUTPUTLISTLEN)
813 s->outputlists[s->noutputlists++] = snewn(OUTPUTLISTLEN,
814 struct output);
815 o = s->outputlists[s->noutputs / OUTPUTLISTLEN] +
816 s->noutputs % OUTPUTLISTLEN;
817
818 o->number = ss->numbers[2*index];
819 o->set = ss;
820 o->index = index;
821 o->npaths = ss->npaths;
822 o2 = add234(s->outputtree, o);
823 if (o2 != o) {
824 o2->npaths += o->npaths;
825 } else {
826 s->noutputs++;
827 }
828 *n = o->number;
829 return TRUE;
830}
831
832static struct sets *do_search(int ninputs, int *inputs,
833 const struct rules *rules, int *target,
834 int debug, int multiple)
835{
836 struct sets *s;
837 struct set *sn;
838 int qpos, i;
839 const struct operation *const *ops = rules->ops;
840
841 s = snew(struct sets);
842 s->setlists = NULL;
843 s->nsets = s->nsetlists = s->setlistsize = 0;
844 s->numberlists = NULL;
845 s->nnumbers = s->nnumberlists = s->numberlistsize = 0;
846 s->outputlists = NULL;
847 s->noutputs = s->noutputlists = s->outputlistsize = 0;
848 s->settree = newtree234(setcmp);
849 s->outputtree = newtree234(outputcmp);
850 s->ops = ops;
851
852 /*
853 * Start with the input set.
854 */
855 sn = newset(s, ninputs, SETFLAG_CONCAT);
856 for (i = 0; i < ninputs; i++) {
857 int newnumber[2];
858 newnumber[0] = inputs[i];
859 newnumber[1] = 1;
860 addtoset(sn, newnumber);
861 }
862 addset(s, sn, multiple, NULL, 0, 0, 0, 0);
863
864 /*
865 * Now perform the breadth-first search: keep looping over sets
866 * until we run out of steam.
867 */
868 qpos = 0;
869 while (qpos < s->nsets) {
870 struct set *ss = s->setlists[qpos / SETLISTLEN] + qpos % SETLISTLEN;
871 struct set *sn;
872 int i, j, k, m;
873
874 if (debug) {
875 int i;
876 printf("processing set:");
877 for (i = 0; i < ss->nnumbers; i++) {
878 printf(" %d", ss->numbers[2*i]);
879 if (ss->numbers[2*i+1] != 1)
880 printf("/%d", ss->numbers[2*i+1]);
881 }
882 printf("\n");
883 }
884
885 /*
886 * Record all the valid output numbers in this state. We
887 * can always do this if there's only one number in the
888 * state; otherwise, we can only do it if we aren't
889 * required to use all the numbers in coming to our answer.
890 */
891 if (ss->nnumbers == 1 || !rules->use_all) {
892 for (i = 0; i < ss->nnumbers; i++) {
893 int n;
894
895 if (addoutput(s, ss, i, &n) && target && n == *target)
896 return s;
897 }
898 }
899
900 /*
901 * Try every possible operation from this state.
902 */
903 for (k = 0; ops[k] && ops[k]->perform; k++) {
904 if ((ops[k]->flags & OPFLAG_NEEDS_CONCAT) &&
905 !(ss->flags & SETFLAG_CONCAT))
906 continue; /* can't use this operation here */
907 for (i = 0; i < ss->nnumbers; i++) {
908 int jlimit = (ops[k]->flags & OPFLAG_UNARY ? 1 : ss->nnumbers);
909 for (j = 0; j < jlimit; j++) {
910 int n[2], newnn = ss->nnumbers;
911 int pa, po, pb, pr;
912
913 if (!(ops[k]->flags & OPFLAG_UNARY)) {
914 if (i == j)
915 continue; /* can't combine a number with itself */
916 if (i > j && ops[k]->commutes)
917 continue; /* no need to do this both ways round */
918 newnn--;
919 }
920 if (!ops[k]->perform(ss->numbers+2*i, ss->numbers+2*j, n))
921 continue; /* operation failed */
922
923 sn = newset(s, newnn, ss->flags);
924
925 if (!(ops[k]->flags & OPFLAG_KEEPS_CONCAT))
926 sn->flags &= ~SETFLAG_CONCAT;
927
928 for (m = 0; m < ss->nnumbers; m++) {
929 if (m == i || (!(ops[k]->flags & OPFLAG_UNARY) &&
930 m == j))
931 continue;
932 sn->numbers[2*sn->nnumbers] = ss->numbers[2*m];
933 sn->numbers[2*sn->nnumbers + 1] = ss->numbers[2*m + 1];
934 sn->nnumbers++;
935 }
936 pa = i;
937 if (ops[k]->flags & OPFLAG_UNARY)
938 pb = sn->nnumbers+10;
939 else
940 pb = j;
941 po = k;
942 pr = addtoset(sn, n);
943 addset(s, sn, multiple, ss, pa, po, pb, pr);
944 if (debug) {
945 int i;
946 if (ops[k]->flags & OPFLAG_UNARYPREFIX)
947 printf(" %s %d ->", ops[po]->dbgtext, pa);
948 else if (ops[k]->flags & OPFLAG_UNARY)
949 printf(" %d %s ->", pa, ops[po]->dbgtext);
950 else
951 printf(" %d %s %d ->", pa, ops[po]->dbgtext, pb);
952 for (i = 0; i < sn->nnumbers; i++) {
953 printf(" %d", sn->numbers[2*i]);
954 if (sn->numbers[2*i+1] != 1)
955 printf("/%d", sn->numbers[2*i+1]);
956 }
957 printf("\n");
958 }
959 }
960 }
961 }
962
963 qpos++;
964 }
965
966 return s;
967}
968
969static void free_sets(struct sets *s)
970{
971 int i;
972
973 freetree234(s->settree);
974 freetree234(s->outputtree);
975 for (i = 0; i < s->nsetlists; i++)
976 sfree(s->setlists[i]);
977 sfree(s->setlists);
978 for (i = 0; i < s->nnumberlists; i++)
979 sfree(s->numberlists[i]);
980 sfree(s->numberlists);
981 for (i = 0; i < s->noutputlists; i++)
982 sfree(s->outputlists[i]);
983 sfree(s->outputlists);
984 sfree(s);
985}
986
987/*
988 * Print a text formula for producing a given output.
989 */
990void print_recurse(struct sets *s, struct set *ss, int pathindex, int index,
991 int priority, int assoc, int child);
992void print_recurse_inner(struct sets *s, struct set *ss,
993 struct ancestor *a, int pathindex, int index,
994 int priority, int assoc, int child)
995{
996 if (a->prev && index != a->pr) {
997 int pi;
998
999 /*
1000 * This number was passed straight down from this set's
1001 * predecessor. Find its index in the previous set and
1002 * recurse to there.
1003 */
1004 pi = index;
1005 assert(pi != a->pr);
1006 if (pi > a->pr)
1007 pi--;
1008 if (pi >= min(a->pa, a->pb)) {
1009 pi++;
1010 if (pi >= max(a->pa, a->pb))
1011 pi++;
1012 }
1013 print_recurse(s, a->prev, pathindex, pi, priority, assoc, child);
1014 } else if (a->prev && index == a->pr &&
1015 s->ops[a->po]->display) {
1016 /*
1017 * This number was created by a displayed operator in the
1018 * transition from this set to its predecessor. Hence we
1019 * write an open paren, then recurse into the first
1020 * operand, then write the operator, then the second
1021 * operand, and finally close the paren.
1022 */
1023 char *op;
1024 int parens, thispri, thisassoc;
1025
1026 /*
1027 * Determine whether we need parentheses.
1028 */
1029 thispri = s->ops[a->po]->priority;
1030 thisassoc = s->ops[a->po]->assoc;
1031 parens = (thispri < priority ||
1032 (thispri == priority && (assoc & child)));
1033
1034 if (parens)
1035 putchar('(');
1036
1037 if (s->ops[a->po]->flags & OPFLAG_UNARYPREFIX)
1038 for (op = s->ops[a->po]->text; *op; op++)
1039 putchar(*op);
1040
1041 if (s->ops[a->po]->flags & OPFLAG_FN)
1042 putchar('(');
1043
1044 print_recurse(s, a->prev, pathindex, a->pa, thispri, thisassoc, 1);
1045
1046 if (s->ops[a->po]->flags & OPFLAG_FN)
1047 putchar(')');
1048
1049 if (!(s->ops[a->po]->flags & OPFLAG_UNARYPREFIX))
1050 for (op = s->ops[a->po]->text; *op; op++)
1051 putchar(*op);
1052
1053 if (!(s->ops[a->po]->flags & OPFLAG_UNARY))
1054 print_recurse(s, a->prev, pathindex, a->pb, thispri, thisassoc, 2);
1055
1056 if (parens)
1057 putchar(')');
1058 } else {
1059 /*
1060 * This number is either an original, or something formed
1061 * by a non-displayed operator (concatenation). Either way,
1062 * we display it as is.
1063 */
1064 printf("%d", ss->numbers[2*index]);
1065 if (ss->numbers[2*index+1] != 1)
1066 printf("/%d", ss->numbers[2*index+1]);
1067 }
1068}
1069void print_recurse(struct sets *s, struct set *ss, int pathindex, int index,
1070 int priority, int assoc, int child)
1071{
1072 if (!ss->a.prev || pathindex < ss->a.prev->npaths) {
1073 print_recurse_inner(s, ss, &ss->a, pathindex,
1074 index, priority, assoc, child);
1075 } else {
1076 int i;
1077 pathindex -= ss->a.prev->npaths;
1078 for (i = 0; i < ss->nas; i++) {
1079 if (pathindex < ss->as[i].prev->npaths) {
1080 print_recurse_inner(s, ss, &ss->as[i], pathindex,
1081 index, priority, assoc, child);
1082 break;
1083 }
1084 pathindex -= ss->as[i].prev->npaths;
1085 }
1086 }
1087}
1088void print(int pathindex, struct sets *s, struct output *o)
1089{
1090 print_recurse(s, o->set, pathindex, o->index, 0, 0, 0);
1091}
1092
1093/*
1094 * gcc -g -O0 -o numgame numgame.c -I.. ../{malloc,tree234,nullfe}.c -lm
1095 */
1096int main(int argc, char **argv)
1097{
1098 int doing_opts = TRUE;
1099 const struct rules *rules = NULL;
1100 char *pname = argv[0];
1101 int got_target = FALSE, target = 0;
1102 int numbers[10], nnumbers = 0;
1103 int verbose = FALSE;
1104 int pathcounts = FALSE;
1105 int multiple = FALSE;
1106 int debug_bfs = FALSE;
1107 int got_range = FALSE, rangemin = 0, rangemax = 0;
1108
1109 struct output *o;
1110 struct sets *s;
1111 int i, start, limit;
1112
1113 while (--argc) {
1114 char *p = *++argv;
1115 int c;
1116
1117 if (doing_opts && *p == '-') {
1118 p++;
1119
1120 if (!strcmp(p, "-")) {
1121 doing_opts = FALSE;
1122 continue;
1123 } else if (*p == '-') {
1124 p++;
1125 if (!strcmp(p, "debug-bfs")) {
1126 debug_bfs = TRUE;
1127 } else {
1128 fprintf(stderr, "%s: option '--%s' not recognised\n",
1129 pname, p);
1130 }
1131 } else while (p && *p) switch (c = *p++) {
1132 case 'C':
1133 rules = &rules_countdown;
1134 break;
1135 case 'B':
1136 rules = &rules_3388;
1137 break;
1138 case 'D':
1139 rules = &rules_four4s;
1140 break;
1141 case 'A':
1142 rules = &rules_anythinggoes;
1143 break;
1144 case 'v':
1145 verbose = TRUE;
1146 break;
1147 case 'p':
1148 pathcounts = TRUE;
1149 break;
1150 case 'm':
1151 multiple = TRUE;
1152 break;
1153 case 't':
1154 case 'r':
1155 {
1156 char *v;
1157 if (*p) {
1158 v = p;
1159 p = NULL;
1160 } else if (--argc) {
1161 v = *++argv;
1162 } else {
1163 fprintf(stderr, "%s: option '-%c' expects an"
1164 " argument\n", pname, c);
1165 return 1;
1166 }
1167 switch (c) {
1168 case 't':
1169 got_target = TRUE;
1170 target = atoi(v);
1171 break;
1172 case 'r':
1173 {
1174 char *sep = strchr(v, '-');
1175 got_range = TRUE;
1176 if (sep) {
1177 rangemin = atoi(v);
1178 rangemax = atoi(sep+1);
1179 } else {
1180 rangemin = 0;
1181 rangemax = atoi(v);
1182 }
1183 }
1184 break;
1185 }
1186 }
1187 break;
1188 default:
1189 fprintf(stderr, "%s: option '-%c' not"
1190 " recognised\n", pname, c);
1191 return 1;
1192 }
1193 } else {
1194 if (nnumbers >= lenof(numbers)) {
1195 fprintf(stderr, "%s: internal limit of %d numbers exceeded\n",
1196 pname, (int)lenof(numbers));
1197 return 1;
1198 } else {
1199 numbers[nnumbers++] = atoi(p);
1200 }
1201 }
1202 }
1203
1204 if (!rules) {
1205 fprintf(stderr, "%s: no rule set specified; use -C,-B,-D,-A\n", pname);
1206 return 1;
1207 }
1208
1209 if (!nnumbers) {
1210 fprintf(stderr, "%s: no input numbers specified\n", pname);
1211 return 1;
1212 }
1213
1214 if (got_range) {
1215 if (got_target) {
1216 fprintf(stderr, "%s: only one of -t and -r may be specified\n", pname);
1217 return 1;
1218 }
1219 if (rangemin >= rangemax) {
1220 fprintf(stderr, "%s: range not sensible (%d - %d)\n", pname, rangemin, rangemax);
1221 return 1;
1222 }
1223 }
1224
1225 s = do_search(nnumbers, numbers, rules, (got_target ? &target : NULL),
1226 debug_bfs, multiple);
1227
1228 if (got_target) {
1229 o = findrelpos234(s->outputtree, &target, outputfindcmp,
1230 REL234_LE, &start);
1231 if (!o)
1232 start = -1;
1233 o = findrelpos234(s->outputtree, &target, outputfindcmp,
1234 REL234_GE, &limit);
1235 if (!o)
1236 limit = -1;
1237 assert(start != -1 || limit != -1);
1238 if (start == -1)
1239 start = limit;
1240 else if (limit == -1)
1241 limit = start;
1242 limit++;
1243 } else if (got_range) {
1244 if (!findrelpos234(s->outputtree, &rangemin, outputfindcmp,
1245 REL234_GE, &start) ||
1246 !findrelpos234(s->outputtree, &rangemax, outputfindcmp,
1247 REL234_LE, &limit)) {
1248 printf("No solutions available in specified range %d-%d\n", rangemin, rangemax);
1249 return 1;
1250 }
1251 limit++;
1252 } else {
1253 start = 0;
1254 limit = count234(s->outputtree);
1255 }
1256
1257 for (i = start; i < limit; i++) {
1258 char buf[256];
1259
1260 o = index234(s->outputtree, i);
1261
1262 sprintf(buf, "%d", o->number);
1263
1264 if (pathcounts)
1265 sprintf(buf + strlen(buf), " [%d]", o->npaths);
1266
1267 if (got_target || verbose) {
1268 int j, npaths;
1269
1270 if (multiple)
1271 npaths = o->npaths;
1272 else
1273 npaths = 1;
1274
1275 for (j = 0; j < npaths; j++) {
1276 printf("%s = ", buf);
1277 print(j, s, o);
1278 putchar('\n');
1279 }
1280 } else {
1281 printf("%s\n", buf);
1282 }
1283 }
1284
1285 free_sets(s);
1286
1287 return 0;
1288}
1289
1290/* 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..61d6c61c6a
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/path.c
@@ -0,0 +1,786 @@
1/*
2 * Experimental grid generator for Nikoli's `Number Link' puzzle.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <assert.h>
8#include "puzzles.h"
9
10/*
11 * 2005-07-08: This is currently a Path grid generator which will
12 * construct valid grids at a plausible speed. However, the grids
13 * are not of suitable quality to be used directly as puzzles.
14 *
15 * The basic strategy is to start with an empty grid, and
16 * repeatedly either (a) add a new path to it, or (b) extend one
17 * end of a path by one square in some direction and push other
18 * paths into new shapes in the process. The effect of this is that
19 * we are able to construct a set of paths which between them fill
20 * the entire grid.
21 *
22 * Quality issues: if we set the main loop to do (a) where possible
23 * and (b) only where necessary, we end up with a grid containing a
24 * few too many small paths, which therefore doesn't make for an
25 * interesting puzzle. If we reverse the priority so that we do (b)
26 * where possible and (a) only where necessary, we end up with some
27 * staggeringly interwoven grids with very very few separate paths,
28 * but the result of this is that there's invariably a solution
29 * other than the intended one which leaves many grid squares
30 * unfilled. There's also a separate problem which is that many
31 * grids have really boring and obvious paths in them, such as the
32 * entire bottom row of the grid being taken up by a single path.
33 *
34 * It's not impossible that a few tweaks might eliminate or reduce
35 * the incidence of boring paths, and might also find a happy
36 * medium between too many and too few. There remains the question
37 * of unique solutions, however. I fear there is no alternative but
38 * to write - somehow! - a solver.
39 *
40 * While I'm here, some notes on UI strategy for the parts of the
41 * puzzle implementation that _aren't_ the generator:
42 *
43 * - data model is to track connections between adjacent squares,
44 * so that you aren't limited to extending a path out from each
45 * number but can also mark sections of path which you know
46 * _will_ come in handy later.
47 *
48 * - user interface is to click in one square and drag to an
49 * adjacent one, thus creating a link between them. We can
50 * probably tolerate rapid mouse motion causing a drag directly
51 * to a square which is a rook move away, but any other rapid
52 * motion is ambiguous and probably the best option is to wait
53 * until the mouse returns to a square we know how to reach.
54 *
55 * - a drag causing the current path to backtrack has the effect
56 * of removing bits of it.
57 *
58 * - the UI should enforce at all times the constraint that at
59 * most two links can come into any square.
60 *
61 * - my Cunning Plan for actually implementing this: the game_ui
62 * contains a grid-sized array, which is copied from the current
63 * game_state on starting a drag. While a drag is active, the
64 * contents of the game_ui is adjusted with every mouse motion,
65 * and is displayed _in place_ of the game_state itself. On
66 * termination of a drag, the game_ui array is copied back into
67 * the new game_state (or rather, a string move is encoded which
68 * has precisely the set of link changes to cause that effect).
69 */
70
71/*
72 * Standard notation for directions.
73 */
74#define L 0
75#define U 1
76#define R 2
77#define D 3
78#define DX(dir) ( (dir)==L ? -1 : (dir)==R ? +1 : 0)
79#define DY(dir) ( (dir)==U ? -1 : (dir)==D ? +1 : 0)
80
81/*
82 * Perform a breadth-first search over a grid of squares with the
83 * colour of square (X,Y) given by grid[Y*w+X]. The search begins
84 * at (x,y), and finds all squares which are the same colour as
85 * (x,y) and reachable from it by orthogonal moves. On return:
86 * - dist[Y*w+X] gives the distance of (X,Y) from (x,y), or -1 if
87 * unreachable or a different colour
88 * - the returned value is the number of reachable squares,
89 * including (x,y) itself
90 * - list[0] up to list[returned value - 1] list those squares, in
91 * increasing order of distance from (x,y) (and in arbitrary
92 * order within that).
93 */
94static int bfs(int w, int h, int *grid, int x, int y, int *dist, int *list)
95{
96 int i, j, c, listsize, listdone;
97
98 /*
99 * Start by clearing the output arrays.
100 */
101 for (i = 0; i < w*h; i++)
102 dist[i] = list[i] = -1;
103
104 /*
105 * Set up the initial list.
106 */
107 listsize = 1;
108 listdone = 0;
109 list[0] = y*w+x;
110 dist[y*w+x] = 0;
111 c = grid[y*w+x];
112
113 /*
114 * Repeatedly process a square and add any extra squares to the
115 * end of list.
116 */
117 while (listdone < listsize) {
118 i = list[listdone++];
119 y = i / w;
120 x = i % w;
121 for (j = 0; j < 4; j++) {
122 int xx, yy, ii;
123
124 xx = x + DX(j);
125 yy = y + DY(j);
126 ii = yy*w+xx;
127
128 if (xx >= 0 && xx < w && yy >= 0 && yy < h &&
129 grid[ii] == c && dist[ii] == -1) {
130 dist[ii] = dist[i] + 1;
131 assert(listsize < w*h);
132 list[listsize++] = ii;
133 }
134 }
135 }
136
137 return listsize;
138}
139
140struct genctx {
141 int w, h;
142 int *grid, *sparegrid, *sparegrid2, *sparegrid3;
143 int *dist, *list;
144
145 int npaths, pathsize;
146 int *pathends, *sparepathends; /* 2*npaths entries */
147 int *pathspare; /* npaths entries */
148 int *extends; /* 8*npaths entries */
149};
150
151static struct genctx *new_genctx(int w, int h)
152{
153 struct genctx *ctx = snew(struct genctx);
154 ctx->w = w;
155 ctx->h = h;
156 ctx->grid = snewn(w * h, int);
157 ctx->sparegrid = snewn(w * h, int);
158 ctx->sparegrid2 = snewn(w * h, int);
159 ctx->sparegrid3 = snewn(w * h, int);
160 ctx->dist = snewn(w * h, int);
161 ctx->list = snewn(w * h, int);
162 ctx->npaths = ctx->pathsize = 0;
163 ctx->pathends = ctx->sparepathends = ctx->pathspare = ctx->extends = NULL;
164 return ctx;
165}
166
167static void free_genctx(struct genctx *ctx)
168{
169 sfree(ctx->grid);
170 sfree(ctx->sparegrid);
171 sfree(ctx->sparegrid2);
172 sfree(ctx->sparegrid3);
173 sfree(ctx->dist);
174 sfree(ctx->list);
175 sfree(ctx->pathends);
176 sfree(ctx->sparepathends);
177 sfree(ctx->pathspare);
178 sfree(ctx->extends);
179}
180
181static int newpath(struct genctx *ctx)
182{
183 int n;
184
185 n = ctx->npaths++;
186 if (ctx->npaths > ctx->pathsize) {
187 ctx->pathsize += 16;
188 ctx->pathends = sresize(ctx->pathends, ctx->pathsize*2, int);
189 ctx->sparepathends = sresize(ctx->sparepathends, ctx->pathsize*2, int);
190 ctx->pathspare = sresize(ctx->pathspare, ctx->pathsize, int);
191 ctx->extends = sresize(ctx->extends, ctx->pathsize*8, int);
192 }
193 return n;
194}
195
196static int is_endpoint(struct genctx *ctx, int x, int y)
197{
198 int w = ctx->w, h = ctx->h, c;
199
200 assert(x >= 0 && x < w && y >= 0 && y < h);
201
202 c = ctx->grid[y*w+x];
203 if (c < 0)
204 return FALSE; /* empty square is not an endpoint! */
205 assert(c >= 0 && c < ctx->npaths);
206 if (ctx->pathends[c*2] == y*w+x || ctx->pathends[c*2+1] == y*w+x)
207 return TRUE;
208 return FALSE;
209}
210
211/*
212 * Tries to extend a path by one square in the given direction,
213 * pushing other paths around if necessary. Returns TRUE on success
214 * or FALSE on failure.
215 */
216static int extend_path(struct genctx *ctx, int path, int end, int direction)
217{
218 int w = ctx->w, h = ctx->h;
219 int x, y, xe, ye, cut;
220 int i, j, jp, n, first, last;
221
222 assert(path >= 0 && path < ctx->npaths);
223 assert(end == 0 || end == 1);
224
225 /*
226 * Find the endpoint of the path and the point we plan to
227 * extend it into.
228 */
229 y = ctx->pathends[path * 2 + end] / w;
230 x = ctx->pathends[path * 2 + end] % w;
231 assert(x >= 0 && x < w && y >= 0 && y < h);
232
233 xe = x + DX(direction);
234 ye = y + DY(direction);
235 if (xe < 0 || xe >= w || ye < 0 || ye >= h)
236 return FALSE; /* could not extend in this direction */
237
238 /*
239 * We don't extend paths _directly_ into endpoints of other
240 * paths, although we don't mind too much if a knock-on effect
241 * of an extension is to push part of another path into a third
242 * path's endpoint.
243 */
244 if (is_endpoint(ctx, xe, ye))
245 return FALSE;
246
247 /*
248 * We can't extend a path back the way it came.
249 */
250 if (ctx->grid[ye*w+xe] == path)
251 return FALSE;
252
253 /*
254 * Paths may not double back on themselves. Check if the new
255 * point is adjacent to any point of this path other than (x,y).
256 */
257 for (j = 0; j < 4; j++) {
258 int xf, yf;
259
260 xf = xe + DX(j);
261 yf = ye + DY(j);
262
263 if (xf >= 0 && xf < w && yf >= 0 && yf < h &&
264 (xf != x || yf != y) && ctx->grid[yf*w+xf] == path)
265 return FALSE;
266 }
267
268 /*
269 * Now we're convinced it's valid to _attempt_ the extension.
270 * It may still fail if we run out of space to push other paths
271 * into.
272 *
273 * So now we can set up our temporary data structures. We will
274 * need:
275 *
276 * - a spare copy of the grid on which to gradually move paths
277 * around (sparegrid)
278 *
279 * - a second spare copy with which to remember how paths
280 * looked just before being cut (sparegrid2). FIXME: is
281 * sparegrid2 necessary? right now it's never different from
282 * grid itself
283 *
284 * - a third spare copy with which to do the internal
285 * calculations involved in reconstituting a cut path
286 * (sparegrid3)
287 *
288 * - something to track which paths currently need
289 * reconstituting after being cut, and which have already
290 * been cut (pathspare)
291 *
292 * - a spare copy of pathends to store the altered states in
293 * (sparepathends)
294 */
295 memcpy(ctx->sparegrid, ctx->grid, w*h*sizeof(int));
296 memcpy(ctx->sparegrid2, ctx->grid, w*h*sizeof(int));
297 memcpy(ctx->sparepathends, ctx->pathends, ctx->npaths*2*sizeof(int));
298 for (i = 0; i < ctx->npaths; i++)
299 ctx->pathspare[i] = 0; /* 0=untouched, 1=broken, 2=fixed */
300
301 /*
302 * Working in sparegrid, actually extend the path. If it cuts
303 * another, begin a loop in which we restore any cut path by
304 * moving it out of the way.
305 */
306 cut = ctx->sparegrid[ye*w+xe];
307 ctx->sparegrid[ye*w+xe] = path;
308 ctx->sparepathends[path*2+end] = ye*w+xe;
309 ctx->pathspare[path] = 2; /* this one is sacrosanct */
310 if (cut >= 0) {
311 assert(cut >= 0 && cut < ctx->npaths);
312 ctx->pathspare[cut] = 1; /* broken */
313
314 while (1) {
315 for (i = 0; i < ctx->npaths; i++)
316 if (ctx->pathspare[i] == 1)
317 break;
318 if (i == ctx->npaths)
319 break; /* we're done */
320
321 /*
322 * Path i needs restoring. So walk along its original
323 * track (as given in sparegrid2) and see where it's
324 * been cut. Where it has, surround the cut points in
325 * the same colour, without overwriting already-fixed
326 * paths.
327 */
328 memcpy(ctx->sparegrid3, ctx->sparegrid, w*h*sizeof(int));
329 n = bfs(w, h, ctx->sparegrid2,
330 ctx->pathends[i*2] % w, ctx->pathends[i*2] / w,
331 ctx->dist, ctx->list);
332 first = last = -1;
333if (ctx->sparegrid3[ctx->pathends[i*2]] != i ||
334 ctx->sparegrid3[ctx->pathends[i*2+1]] != i) return FALSE;/* FIXME */
335 for (j = 0; j < n; j++) {
336 jp = ctx->list[j];
337 assert(ctx->dist[jp] == j);
338 assert(ctx->sparegrid2[jp] == i);
339
340 /*
341 * Wipe out the original path in sparegrid.
342 */
343 if (ctx->sparegrid[jp] == i)
344 ctx->sparegrid[jp] = -1;
345
346 /*
347 * Be prepared to shorten the path at either end if
348 * the endpoints have been stomped on.
349 */
350 if (ctx->sparegrid3[jp] == i) {
351 if (first < 0)
352 first = jp;
353 last = jp;
354 }
355
356 if (ctx->sparegrid3[jp] != i) {
357 int jx = jp % w, jy = jp / w;
358 int dx, dy;
359 for (dy = -1; dy <= +1; dy++)
360 for (dx = -1; dx <= +1; dx++) {
361 int newp, newv;
362 if (!dy && !dx)
363 continue; /* central square */
364 if (jx+dx < 0 || jx+dx >= w ||
365 jy+dy < 0 || jy+dy >= h)
366 continue; /* out of range */
367 newp = (jy+dy)*w+(jx+dx);
368 newv = ctx->sparegrid3[newp];
369 if (newv >= 0 && (newv == i ||
370 ctx->pathspare[newv] == 2))
371 continue; /* can't use this square */
372 ctx->sparegrid3[newp] = i;
373 }
374 }
375 }
376
377 if (first < 0 || last < 0)
378 return FALSE; /* path is completely wiped out! */
379
380 /*
381 * Now we've covered sparegrid3 in possible squares for
382 * the new layout of path i. Find the actual layout
383 * we're going to use by bfs: we want the shortest path
384 * from one endpoint to the other.
385 */
386 n = bfs(w, h, ctx->sparegrid3, first % w, first / w,
387 ctx->dist, ctx->list);
388 if (ctx->dist[last] < 2) {
389 /*
390 * Either there is no way to get between the path's
391 * endpoints, or the remaining endpoints simply
392 * aren't far enough apart to make the path viable
393 * any more. This means the entire push operation
394 * has failed.
395 */
396 return FALSE;
397 }
398
399 /*
400 * Write the new path into sparegrid. Also save the new
401 * endpoint locations, in case they've changed.
402 */
403 jp = last;
404 j = ctx->dist[jp];
405 while (1) {
406 int d;
407
408 if (ctx->sparegrid[jp] >= 0) {
409 if (ctx->pathspare[ctx->sparegrid[jp]] == 2)
410 return FALSE; /* somehow we've hit a fixed path */
411 ctx->pathspare[ctx->sparegrid[jp]] = 1; /* broken */
412 }
413 ctx->sparegrid[jp] = i;
414
415 if (j == 0)
416 break;
417
418 /*
419 * Now look at the neighbours of jp to find one
420 * which has dist[] one less.
421 */
422 for (d = 0; d < 4; d++) {
423 int jx = (jp % w) + DX(d), jy = (jp / w) + DY(d);
424 if (jx >= 0 && jx < w && jy >= 0 && jy < w &&
425 ctx->dist[jy*w+jx] == j-1) {
426 jp = jy*w+jx;
427 j--;
428 break;
429 }
430 }
431 assert(d < 4);
432 }
433
434 ctx->sparepathends[i*2] = first;
435 ctx->sparepathends[i*2+1] = last;
436//printf("new ends of path %d: %d,%d\n", i, first, last);
437 ctx->pathspare[i] = 2; /* fixed */
438 }
439 }
440
441 /*
442 * If we got here, the extension was successful!
443 */
444 memcpy(ctx->grid, ctx->sparegrid, w*h*sizeof(int));
445 memcpy(ctx->pathends, ctx->sparepathends, ctx->npaths*2*sizeof(int));
446 return TRUE;
447}
448
449/*
450 * Tries to add a new path to the grid.
451 */
452static int add_path(struct genctx *ctx, random_state *rs)
453{
454 int w = ctx->w, h = ctx->h;
455 int i, ii, n;
456
457 /*
458 * Our strategy is:
459 * - randomly choose an empty square in the grid
460 * - do a BFS from that point to find a long path starting
461 * from it
462 * - if we run out of viable empty squares, return failure.
463 */
464
465 /*
466 * Use `sparegrid' to collect a list of empty squares.
467 */
468 n = 0;
469 for (i = 0; i < w*h; i++)
470 if (ctx->grid[i] == -1)
471 ctx->sparegrid[n++] = i;
472
473 /*
474 * Shuffle the grid.
475 */
476 for (i = n; i-- > 1 ;) {
477 int k = random_upto(rs, i+1);
478 if (k != i) {
479 int t = ctx->sparegrid[i];
480 ctx->sparegrid[i] = ctx->sparegrid[k];
481 ctx->sparegrid[k] = t;
482 }
483 }
484
485 /*
486 * Loop over it trying to add paths. This looks like a
487 * horrifying N^4 algorithm (that is, (w*h)^2), but I predict
488 * that in fact the worst case will very rarely arise because
489 * when there's lots of grid space an attempt will succeed very
490 * quickly.
491 */
492 for (ii = 0; ii < n; ii++) {
493 int i = ctx->sparegrid[ii];
494 int y = i / w, x = i % w, nsq;
495 int r, c, j;
496
497 /*
498 * BFS from here to find long paths.
499 */
500 nsq = bfs(w, h, ctx->grid, x, y, ctx->dist, ctx->list);
501
502 /*
503 * If there aren't any long enough, give up immediately.
504 */
505 assert(nsq > 0); /* must be the start square at least! */
506 if (ctx->dist[ctx->list[nsq-1]] < 3)
507 continue;
508
509 /*
510 * Find the first viable endpoint in ctx->list (i.e. the
511 * first point with distance at least three). I could
512 * binary-search for this, but that would be O(log N)
513 * whereas in fact I can get a constant time bound by just
514 * searching up from the start - after all, there can be at
515 * most 13 points at _less_ than distance 3 from the
516 * starting one!
517 */
518 for (j = 0; j < nsq; j++)
519 if (ctx->dist[ctx->list[j]] >= 3)
520 break;
521 assert(j < nsq); /* we tested above that there was one */
522
523 /*
524 * Now we know that any element of `list' between j and nsq
525 * would be valid in principle. However, we want a few long
526 * paths rather than many small ones, so select only those
527 * elements which are either the maximum length or one
528 * below it.
529 */
530 while (ctx->dist[ctx->list[j]] + 1 < ctx->dist[ctx->list[nsq-1]])
531 j++;
532 r = j + random_upto(rs, nsq - j);
533 j = ctx->list[r];
534
535 /*
536 * And that's our endpoint. Mark the new path on the grid.
537 */
538 c = newpath(ctx);
539 ctx->pathends[c*2] = i;
540 ctx->pathends[c*2+1] = j;
541 ctx->grid[j] = c;
542 while (j != i) {
543 int d, np, index, pts[4];
544 np = 0;
545 for (d = 0; d < 4; d++) {
546 int xn = (j % w) + DX(d), yn = (j / w) + DY(d);
547 if (xn >= 0 && xn < w && yn >= 0 && yn < w &&
548 ctx->dist[yn*w+xn] == ctx->dist[j] - 1)
549 pts[np++] = yn*w+xn;
550 }
551 if (np > 1)
552 index = random_upto(rs, np);
553 else
554 index = 0;
555 j = pts[index];
556 ctx->grid[j] = c;
557 }
558
559 return TRUE;
560 }
561
562 return FALSE;
563}
564
565/*
566 * The main grid generation loop.
567 */
568static void gridgen_mainloop(struct genctx *ctx, random_state *rs)
569{
570 int w = ctx->w, h = ctx->h;
571 int i, n;
572
573 /*
574 * The generation algorithm doesn't always converge. Loop round
575 * until it does.
576 */
577 while (1) {
578 for (i = 0; i < w*h; i++)
579 ctx->grid[i] = -1;
580 ctx->npaths = 0;
581
582 while (1) {
583 /*
584 * See if the grid is full.
585 */
586 for (i = 0; i < w*h; i++)
587 if (ctx->grid[i] < 0)
588 break;
589 if (i == w*h)
590 return;
591
592#ifdef GENERATION_DIAGNOSTICS
593 {
594 int x, y;
595 for (y = 0; y < h; y++) {
596 printf("|");
597 for (x = 0; x < w; x++) {
598 if (ctx->grid[y*w+x] >= 0)
599 printf("%2d", ctx->grid[y*w+x]);
600 else
601 printf(" .");
602 }
603 printf(" |\n");
604 }
605 }
606#endif
607 /*
608 * Try adding a path.
609 */
610 if (add_path(ctx, rs)) {
611#ifdef GENERATION_DIAGNOSTICS
612 printf("added path\n");
613#endif
614 continue;
615 }
616
617 /*
618 * Try extending a path. First list all the possible
619 * extensions.
620 */
621 for (i = 0; i < ctx->npaths * 8; i++)
622 ctx->extends[i] = i;
623 n = i;
624
625 /*
626 * Then shuffle the list.
627 */
628 for (i = n; i-- > 1 ;) {
629 int k = random_upto(rs, i+1);
630 if (k != i) {
631 int t = ctx->extends[i];
632 ctx->extends[i] = ctx->extends[k];
633 ctx->extends[k] = t;
634 }
635 }
636
637 /*
638 * Now try each one in turn until one works.
639 */
640 for (i = 0; i < n; i++) {
641 int p, d, e;
642 p = ctx->extends[i];
643 d = p % 4;
644 p /= 4;
645 e = p % 2;
646 p /= 2;
647
648#ifdef GENERATION_DIAGNOSTICS
649 printf("trying to extend path %d end %d (%d,%d) in dir %d\n", p, e,
650 ctx->pathends[p*2+e] % w,
651 ctx->pathends[p*2+e] / w, d);
652#endif
653 if (extend_path(ctx, p, e, d)) {
654#ifdef GENERATION_DIAGNOSTICS
655 printf("extended path %d end %d (%d,%d) in dir %d\n", p, e,
656 ctx->pathends[p*2+e] % w,
657 ctx->pathends[p*2+e] / w, d);
658#endif
659 break;
660 }
661 }
662
663 if (i < n)
664 continue;
665
666 break;
667 }
668 }
669}
670
671/*
672 * Wrapper function which deals with the boring bits such as
673 * removing the solution from the generated grid, shuffling the
674 * numeric labels and creating/disposing of the context structure.
675 */
676static int *gridgen(int w, int h, random_state *rs)
677{
678 struct genctx *ctx;
679 int *ret;
680 int i;
681
682 ctx = new_genctx(w, h);
683
684 gridgen_mainloop(ctx, rs);
685
686 /*
687 * There is likely to be an ordering bias in the numbers
688 * (longer paths on lower numbers due to there having been more
689 * grid space when laying them down). So we must shuffle the
690 * numbers. We use ctx->pathspare for this.
691 *
692 * This is also as good a time as any to shift to numbering
693 * from 1, for display to the user.
694 */
695 for (i = 0; i < ctx->npaths; i++)
696 ctx->pathspare[i] = i+1;
697 for (i = ctx->npaths; i-- > 1 ;) {
698 int k = random_upto(rs, i+1);
699 if (k != i) {
700 int t = ctx->pathspare[i];
701 ctx->pathspare[i] = ctx->pathspare[k];
702 ctx->pathspare[k] = t;
703 }
704 }
705
706 /* FIXME: remove this at some point! */
707 {
708 int y, x;
709 for (y = 0; y < h; y++) {
710 printf("|");
711 for (x = 0; x < w; x++) {
712 assert(ctx->grid[y*w+x] >= 0);
713 printf("%2d", ctx->pathspare[ctx->grid[y*w+x]]);
714 }
715 printf(" |\n");
716 }
717 printf("\n");
718 }
719
720 /*
721 * Clear the grid, and write in just the endpoints.
722 */
723 for (i = 0; i < w*h; i++)
724 ctx->grid[i] = 0;
725 for (i = 0; i < ctx->npaths; i++) {
726 ctx->grid[ctx->pathends[i*2]] =
727 ctx->grid[ctx->pathends[i*2+1]] = ctx->pathspare[i];
728 }
729
730 ret = ctx->grid;
731 ctx->grid = NULL;
732
733 free_genctx(ctx);
734
735 return ret;
736}
737
738#ifdef TEST_GEN
739
740#define TEST_GENERAL
741
742int main(void)
743{
744 int w = 10, h = 8;
745 random_state *rs = random_init("12345", 5);
746 int x, y, i, *grid;
747
748 for (i = 0; i < 10; i++) {
749 grid = gridgen(w, h, rs);
750
751 for (y = 0; y < h; y++) {
752 printf("|");
753 for (x = 0; x < w; x++) {
754 if (grid[y*w+x] > 0)
755 printf("%2d", grid[y*w+x]);
756 else
757 printf(" .");
758 }
759 printf(" |\n");
760 }
761 printf("\n");
762
763 sfree(grid);
764 }
765
766 return 0;
767}
768#endif
769
770#ifdef TEST_GENERAL
771#include <stdarg.h>
772
773void fatal(char *fmt, ...)
774{
775 va_list ap;
776
777 fprintf(stderr, "fatal error: ");
778
779 va_start(ap, fmt);
780 vfprintf(stderr, fmt, ap);
781 va_end(ap);
782
783 fprintf(stderr, "\n");
784 exit(1);
785}
786#endif
diff --git a/apps/plugins/puzzles/src/unfinished/separate.R b/apps/plugins/puzzles/src/unfinished/separate.R
new file mode 100644
index 0000000000..f861c8f4fe
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/separate.R
@@ -0,0 +1,21 @@
1# -*- makefile -*-
2
3SEPARATE_EXTRA = divvy dsf
4
5separate : [X] GTK COMMON separate SEPARATE_EXTRA separate-icon|no-icon
6
7separate : [G] WINDOWS COMMON separate SEPARATE_EXTRA separate.res|noicon.res
8
9ALL += separate[COMBINED] SEPARATE_EXTRA
10
11!begin am gtk
12GAMES += separate
13!end
14
15!begin >list.c
16 A(separate) \
17!end
18
19!begin >gamedesc.txt
20separate:separate.exe:Separate:Rectangle-dividing puzzle:Partition the grid into regions containing one of each letter.
21!end
diff --git a/apps/plugins/puzzles/src/unfinished/separate.c b/apps/plugins/puzzles/src/unfinished/separate.c
new file mode 100644
index 0000000000..a7b4fc96e1
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/separate.c
@@ -0,0 +1,859 @@
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#include <math.h>
98
99#include "puzzles.h"
100
101enum {
102 COL_BACKGROUND,
103 NCOLOURS
104};
105
106struct game_params {
107 int w, h, k;
108};
109
110struct game_state {
111 int FIXME;
112};
113
114static game_params *default_params(void)
115{
116 game_params *ret = snew(game_params);
117
118 ret->w = ret->h = ret->k = 5; /* FIXME: a bit bigger? */
119
120 return ret;
121}
122
123static int game_fetch_preset(int i, char **name, game_params **params)
124{
125 return FALSE;
126}
127
128static void free_params(game_params *params)
129{
130 sfree(params);
131}
132
133static game_params *dup_params(const game_params *params)
134{
135 game_params *ret = snew(game_params);
136 *ret = *params; /* structure copy */
137 return ret;
138}
139
140static void decode_params(game_params *params, char const *string)
141{
142 params->w = params->h = params->k = atoi(string);
143 while (*string && isdigit((unsigned char)*string)) string++;
144 if (*string == 'x') {
145 string++;
146 params->h = atoi(string);
147 while (*string && isdigit((unsigned char)*string)) string++;
148 }
149 if (*string == 'n') {
150 string++;
151 params->k = atoi(string);
152 while (*string && isdigit((unsigned char)*string)) string++;
153 }
154}
155
156static char *encode_params(const game_params *params, int full)
157{
158 char buf[256];
159 sprintf(buf, "%dx%dn%d", params->w, params->h, params->k);
160 return dupstr(buf);
161}
162
163static config_item *game_configure(const game_params *params)
164{
165 return NULL;
166}
167
168static game_params *custom_params(const config_item *cfg)
169{
170 return NULL;
171}
172
173static char *validate_params(const game_params *params, int full)
174{
175 return NULL;
176}
177
178/* ----------------------------------------------------------------------
179 * Solver and generator.
180 */
181
182struct solver_scratch {
183 int w, h, k;
184
185 /*
186 * Tracks connectedness between squares.
187 */
188 int *dsf;
189
190 /*
191 * size[dsf_canonify(dsf, yx)] tracks the size of the
192 * connected component containing yx.
193 */
194 int *size;
195
196 /*
197 * contents[dsf_canonify(dsf, yx)*k+i] tracks whether or not
198 * the connected component containing yx includes letter i. If
199 * the value is -1, it doesn't; otherwise its value is the
200 * index in the main grid of the square which contributes that
201 * letter to the component.
202 */
203 int *contents;
204
205 /*
206 * disconnect[dsf_canonify(dsf, yx1)*w*h + dsf_canonify(dsf, yx2)]
207 * tracks whether or not the connected components containing
208 * yx1 and yx2 are known to be distinct.
209 */
210 unsigned char *disconnect;
211
212 /*
213 * Temporary space used only inside particular solver loops.
214 */
215 int *tmp;
216};
217
218struct solver_scratch *solver_scratch_new(int w, int h, int k)
219{
220 int wh = w*h;
221 struct solver_scratch *sc = snew(struct solver_scratch);
222
223 sc->w = w;
224 sc->h = h;
225 sc->k = k;
226
227 sc->dsf = snew_dsf(wh);
228 sc->size = snewn(wh, int);
229 sc->contents = snewn(wh * k, int);
230 sc->disconnect = snewn(wh*wh, unsigned char);
231 sc->tmp = snewn(wh, int);
232
233 return sc;
234}
235
236void solver_scratch_free(struct solver_scratch *sc)
237{
238 sfree(sc->dsf);
239 sfree(sc->size);
240 sfree(sc->contents);
241 sfree(sc->disconnect);
242 sfree(sc->tmp);
243 sfree(sc);
244}
245
246void solver_connect(struct solver_scratch *sc, int yx1, int yx2)
247{
248 int w = sc->w, h = sc->h, k = sc->k;
249 int wh = w*h;
250 int i, yxnew;
251
252 yx1 = dsf_canonify(sc->dsf, yx1);
253 yx2 = dsf_canonify(sc->dsf, yx2);
254 assert(yx1 != yx2);
255
256 /*
257 * To connect two components together into a bigger one, we
258 * start by merging them in the dsf itself.
259 */
260 dsf_merge(sc->dsf, yx1, yx2);
261 yxnew = dsf_canonify(sc->dsf, yx2);
262
263 /*
264 * The size of the new component is the sum of the sizes of the
265 * old ones.
266 */
267 sc->size[yxnew] = sc->size[yx1] + sc->size[yx2];
268
269 /*
270 * The contents bitmap of the new component is the union of the
271 * contents of the old ones.
272 *
273 * Given two numbers at most one of which is not -1, we can
274 * find the other one by adding the two and adding 1; this
275 * will yield -1 if both were -1 to begin with, otherwise the
276 * other.
277 *
278 * (A neater approach would be to take their bitwise AND, but
279 * this is unfortunately not well-defined standard C when done
280 * to signed integers.)
281 */
282 for (i = 0; i < k; i++) {
283 assert(sc->contents[yx1*k+i] < 0 || sc->contents[yx2*k+i] < 0);
284 sc->contents[yxnew*k+i] = (sc->contents[yx1*k+i] +
285 sc->contents[yx2*k+i] + 1);
286 }
287
288 /*
289 * We must combine the rows _and_ the columns in the disconnect
290 * matrix.
291 */
292 for (i = 0; i < wh; i++)
293 sc->disconnect[yxnew*wh+i] = (sc->disconnect[yx1*wh+i] ||
294 sc->disconnect[yx2*wh+i]);
295 for (i = 0; i < wh; i++)
296 sc->disconnect[i*wh+yxnew] = (sc->disconnect[i*wh+yx1] ||
297 sc->disconnect[i*wh+yx2]);
298}
299
300void solver_disconnect(struct solver_scratch *sc, int yx1, int yx2)
301{
302 int w = sc->w, h = sc->h;
303 int wh = w*h;
304
305 yx1 = dsf_canonify(sc->dsf, yx1);
306 yx2 = dsf_canonify(sc->dsf, yx2);
307 assert(yx1 != yx2);
308 assert(!sc->disconnect[yx1*wh+yx2]);
309 assert(!sc->disconnect[yx2*wh+yx1]);
310
311 /*
312 * Mark the components as disconnected from each other in the
313 * disconnect matrix.
314 */
315 sc->disconnect[yx1*wh+yx2] = sc->disconnect[yx2*wh+yx1] = 1;
316}
317
318void solver_init(struct solver_scratch *sc)
319{
320 int w = sc->w, h = sc->h;
321 int wh = w*h;
322 int i;
323
324 /*
325 * Set up most of the scratch space. We don't set up the
326 * contents array, however, because this will change if we
327 * adjust the letter arrangement and re-run the solver.
328 */
329 dsf_init(sc->dsf, wh);
330 for (i = 0; i < wh; i++) sc->size[i] = 1;
331 memset(sc->disconnect, 0, wh*wh);
332}
333
334int solver_attempt(struct solver_scratch *sc, const unsigned char *grid,
335 unsigned char *gen_lock)
336{
337 int w = sc->w, h = sc->h, k = sc->k;
338 int wh = w*h;
339 int i, x, y;
340 int done_something_overall = FALSE;
341
342 /*
343 * Set up the contents array from the grid.
344 */
345 for (i = 0; i < wh*k; i++)
346 sc->contents[i] = -1;
347 for (i = 0; i < wh; i++)
348 sc->contents[dsf_canonify(sc->dsf, i)*k+grid[i]] = i;
349
350 while (1) {
351 int done_something = FALSE;
352
353 /*
354 * Go over the grid looking for reasons to add to the
355 * disconnect matrix. We're after pairs of squares which:
356 *
357 * - are adjacent in the grid
358 * - belong to distinct dsf components
359 * - their components are not already marked as
360 * disconnected
361 * - their components share a letter in common.
362 */
363 for (y = 0; y < h; y++) {
364 for (x = 0; x < w; x++) {
365 int dir;
366 for (dir = 0; dir < 2; dir++) {
367 int x2 = x + dir, y2 = y + 1 - dir;
368 int yx = y*w+x, yx2 = y2*w+x2;
369
370 if (x2 >= w || y2 >= h)
371 continue; /* one square is outside the grid */
372
373 yx = dsf_canonify(sc->dsf, yx);
374 yx2 = dsf_canonify(sc->dsf, yx2);
375 if (yx == yx2)
376 continue; /* same dsf component */
377
378 if (sc->disconnect[yx*wh+yx2])
379 continue; /* already known disconnected */
380
381 for (i = 0; i < k; i++)
382 if (sc->contents[yx*k+i] >= 0 &&
383 sc->contents[yx2*k+i] >= 0)
384 break;
385 if (i == k)
386 continue; /* no letter in common */
387
388 /*
389 * We've found one. Mark yx and yx2 as
390 * disconnected from each other.
391 */
392#ifdef SOLVER_DIAGNOSTICS
393 printf("Disconnecting %d and %d (%c)\n", yx, yx2, 'A'+i);
394#endif
395 solver_disconnect(sc, yx, yx2);
396 done_something = done_something_overall = TRUE;
397
398 /*
399 * We have just made a deduction which hinges
400 * on two particular grid squares being the
401 * same. If we are feeding back to a generator
402 * loop, we must therefore mark those squares
403 * as fixed in the generator, so that future
404 * rearrangement of the grid will not break
405 * the information on which we have already
406 * based deductions.
407 */
408 if (gen_lock) {
409 gen_lock[sc->contents[yx*k+i]] = 1;
410 gen_lock[sc->contents[yx2*k+i]] = 1;
411 }
412 }
413 }
414 }
415
416 /*
417 * Now go over the grid looking for dsf components which
418 * are below maximum size and only have one way to extend,
419 * and extending them.
420 */
421 for (i = 0; i < wh; i++)
422 sc->tmp[i] = -1;
423 for (y = 0; y < h; y++) {
424 for (x = 0; x < w; x++) {
425 int yx = dsf_canonify(sc->dsf, y*w+x);
426 int dir;
427
428 if (sc->size[yx] == k)
429 continue;
430
431 for (dir = 0; dir < 4; dir++) {
432 int x2 = x + (dir==0 ? -1 : dir==2 ? 1 : 0);
433 int y2 = y + (dir==1 ? -1 : dir==3 ? 1 : 0);
434 int yx2, yx2c;
435
436 if (y2 < 0 || y2 >= h || x2 < 0 || x2 >= w)
437 continue;
438 yx2 = y2*w+x2;
439 yx2c = dsf_canonify(sc->dsf, yx2);
440
441 if (yx2c != yx && !sc->disconnect[yx2c*wh+yx]) {
442 /*
443 * Component yx can be extended into square
444 * yx2.
445 */
446 if (sc->tmp[yx] == -1)
447 sc->tmp[yx] = yx2;
448 else if (sc->tmp[yx] != yx2)
449 sc->tmp[yx] = -2; /* multiple choices found */
450 }
451 }
452 }
453 }
454 for (i = 0; i < wh; i++) {
455 if (sc->tmp[i] >= 0) {
456 /*
457 * Make sure we haven't connected the two already
458 * during this loop (which could happen if for
459 * _both_ components this was the only way to
460 * extend them).
461 */
462 if (dsf_canonify(sc->dsf, i) ==
463 dsf_canonify(sc->dsf, sc->tmp[i]))
464 continue;
465
466#ifdef SOLVER_DIAGNOSTICS
467 printf("Connecting %d and %d\n", i, sc->tmp[i]);
468#endif
469 solver_connect(sc, i, sc->tmp[i]);
470 done_something = done_something_overall = TRUE;
471 break;
472 }
473 }
474
475 if (!done_something)
476 break;
477 }
478
479 /*
480 * Return 0 if we haven't made any progress; 1 if we've done
481 * something but not solved it completely; 2 if we've solved
482 * it completely.
483 */
484 for (i = 0; i < wh; i++)
485 if (sc->size[dsf_canonify(sc->dsf, i)] != k)
486 break;
487 if (i == wh)
488 return 2;
489 if (done_something_overall)
490 return 1;
491 return 0;
492}
493
494unsigned char *generate(int w, int h, int k, random_state *rs)
495{
496 int wh = w*h;
497 int n = wh/k;
498 struct solver_scratch *sc;
499 unsigned char *grid;
500 unsigned char *shuffled;
501 int i, j, m, retries;
502 int *permutation;
503 unsigned char *gen_lock;
504 extern int *divvy_rectangle(int w, int h, int k, random_state *rs);
505
506 sc = solver_scratch_new(w, h, k);
507 grid = snewn(wh, unsigned char);
508 shuffled = snewn(k, unsigned char);
509 permutation = snewn(wh, int);
510 gen_lock = snewn(wh, unsigned char);
511
512 do {
513 int *dsf = divvy_rectangle(w, h, k, rs);
514
515 /*
516 * Go through the dsf and find the indices of all the
517 * squares involved in each omino, in a manner conducive
518 * to per-omino indexing. We set permutation[i*k+j] to be
519 * the index of the jth square (ordered arbitrarily) in
520 * omino i.
521 */
522 for (i = j = 0; i < wh; i++)
523 if (dsf_canonify(dsf, i) == i) {
524 sc->tmp[i] = j;
525 /*
526 * During this loop and the following one, we use
527 * the last element of each row of permutation[]
528 * as a counter of the number of indices so far
529 * placed in it. When we place the final index of
530 * an omino, that counter is overwritten, but that
531 * doesn't matter because we'll never use it
532 * again. Of course this depends critically on
533 * divvy_rectangle() having returned correct
534 * results, or else chaos would ensue.
535 */
536 permutation[j*k+k-1] = 0;
537 j++;
538 }
539 for (i = 0; i < wh; i++) {
540 j = sc->tmp[dsf_canonify(dsf, i)];
541 m = permutation[j*k+k-1]++;
542 permutation[j*k+m] = i;
543 }
544
545 /*
546 * Track which squares' letters we have already depended
547 * on for deductions. This is gradually updated by
548 * solver_attempt().
549 */
550 memset(gen_lock, 0, wh);
551
552 /*
553 * Now repeatedly fill the grid with letters, and attempt
554 * to solve it. If the solver makes progress but does not
555 * fail completely, then gen_lock will have been updated
556 * and we try again. On a complete failure, though, we
557 * have no option but to give up and abandon this set of
558 * ominoes.
559 */
560 solver_init(sc);
561 retries = k*k;
562 while (1) {
563 /*
564 * Fill the grid with letters. We can safely use
565 * sc->tmp to hold the set of letters required at each
566 * stage, since it's at least size k and is currently
567 * unused.
568 */
569 for (i = 0; i < n; i++) {
570 /*
571 * First, determine the set of letters already
572 * placed in this omino by gen_lock.
573 */
574 for (j = 0; j < k; j++)
575 sc->tmp[j] = j;
576 for (j = 0; j < k; j++) {
577 int index = permutation[i*k+j];
578 int letter = grid[index];
579 if (gen_lock[index])
580 sc->tmp[letter] = -1;
581 }
582 /*
583 * Now collect together all the remaining letters
584 * and randomly shuffle them.
585 */
586 for (j = m = 0; j < k; j++)
587 if (sc->tmp[j] >= 0)
588 sc->tmp[m++] = sc->tmp[j];
589 shuffle(sc->tmp, m, sizeof(*sc->tmp), rs);
590 /*
591 * Finally, write the shuffled letters into the
592 * grid.
593 */
594 for (j = 0; j < k; j++) {
595 int index = permutation[i*k+j];
596 if (!gen_lock[index])
597 grid[index] = sc->tmp[--m];
598 }
599 assert(m == 0);
600 }
601
602 /*
603 * Now we have a candidate grid. Attempt to progress
604 * the solution.
605 */
606 m = solver_attempt(sc, grid, gen_lock);
607 if (m == 2 || /* success */
608 (m == 0 && retries-- <= 0)) /* failure */
609 break;
610 if (m == 1)
611 retries = k*k; /* reset this counter, and continue */
612 }
613
614 sfree(dsf);
615 } while (m == 0);
616
617 sfree(gen_lock);
618 sfree(permutation);
619 sfree(shuffled);
620 solver_scratch_free(sc);
621
622 return grid;
623}
624
625/* ----------------------------------------------------------------------
626 * End of solver/generator code.
627 */
628
629static char *new_game_desc(const game_params *params, random_state *rs,
630 char **aux, int interactive)
631{
632 int w = params->w, h = params->h, wh = w*h, k = params->k;
633 unsigned char *grid;
634 char *desc;
635 int i;
636
637 grid = generate(w, h, k, rs);
638
639 desc = snewn(wh+1, char);
640 for (i = 0; i < wh; i++)
641 desc[i] = 'A' + grid[i];
642 desc[wh] = '\0';
643
644 sfree(grid);
645
646 return desc;
647}
648
649static char *validate_desc(const game_params *params, const char *desc)
650{
651 return NULL;
652}
653
654static game_state *new_game(midend *me, const game_params *params,
655 const char *desc)
656{
657 game_state *state = snew(game_state);
658
659 state->FIXME = 0;
660
661 return state;
662}
663
664static game_state *dup_game(const game_state *state)
665{
666 game_state *ret = snew(game_state);
667
668 ret->FIXME = state->FIXME;
669
670 return ret;
671}
672
673static void free_game(game_state *state)
674{
675 sfree(state);
676}
677
678static char *solve_game(const game_state *state, const game_state *currstate,
679 const char *aux, char **error)
680{
681 return NULL;
682}
683
684static int game_can_format_as_text_now(const game_params *params)
685{
686 return TRUE;
687}
688
689static char *game_text_format(const game_state *state)
690{
691 return NULL;
692}
693
694static game_ui *new_ui(const game_state *state)
695{
696 return NULL;
697}
698
699static void free_ui(game_ui *ui)
700{
701}
702
703static char *encode_ui(const game_ui *ui)
704{
705 return NULL;
706}
707
708static void decode_ui(game_ui *ui, const char *encoding)
709{
710}
711
712static void game_changed_state(game_ui *ui, const game_state *oldstate,
713 const game_state *newstate)
714{
715}
716
717struct game_drawstate {
718 int tilesize;
719 int FIXME;
720};
721
722static char *interpret_move(const game_state *state, game_ui *ui,
723 const game_drawstate *ds,
724 int x, int y, int button)
725{
726 return NULL;
727}
728
729static game_state *execute_move(const game_state *state, const char *move)
730{
731 return NULL;
732}
733
734/* ----------------------------------------------------------------------
735 * Drawing routines.
736 */
737
738static void game_compute_size(const game_params *params, int tilesize,
739 int *x, int *y)
740{
741 *x = *y = 10 * tilesize; /* FIXME */
742}
743
744static void game_set_size(drawing *dr, game_drawstate *ds,
745 const game_params *params, int tilesize)
746{
747 ds->tilesize = tilesize;
748}
749
750static float *game_colours(frontend *fe, int *ncolours)
751{
752 float *ret = snewn(3 * NCOLOURS, float);
753
754 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
755
756 *ncolours = NCOLOURS;
757 return ret;
758}
759
760static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
761{
762 struct game_drawstate *ds = snew(struct game_drawstate);
763
764 ds->tilesize = 0;
765 ds->FIXME = 0;
766
767 return ds;
768}
769
770static void game_free_drawstate(drawing *dr, game_drawstate *ds)
771{
772 sfree(ds);
773}
774
775static void game_redraw(drawing *dr, game_drawstate *ds,
776 const game_state *oldstate, const game_state *state,
777 int dir, const game_ui *ui,
778 float animtime, float flashtime)
779{
780 /*
781 * The initial contents of the window are not guaranteed and
782 * can vary with front ends. To be on the safe side, all games
783 * should start by drawing a big background-colour rectangle
784 * covering the whole window.
785 */
786 draw_rect(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize, COL_BACKGROUND);
787}
788
789static float game_anim_length(const game_state *oldstate,
790 const game_state *newstate, int dir, game_ui *ui)
791{
792 return 0.0F;
793}
794
795static float game_flash_length(const game_state *oldstate,
796 const game_state *newstate, int dir, game_ui *ui)
797{
798 return 0.0F;
799}
800
801static int game_status(const game_state *state)
802{
803 return 0;
804}
805
806static int game_timing_state(const game_state *state, game_ui *ui)
807{
808 return TRUE;
809}
810
811static void game_print_size(const game_params *params, float *x, float *y)
812{
813}
814
815static void game_print(drawing *dr, const game_state *state, int tilesize)
816{
817}
818
819#ifdef COMBINED
820#define thegame separate
821#endif
822
823const struct game thegame = {
824 "Separate", NULL, NULL,
825 default_params,
826 game_fetch_preset, NULL,
827 decode_params,
828 encode_params,
829 free_params,
830 dup_params,
831 FALSE, game_configure, custom_params,
832 validate_params,
833 new_game_desc,
834 validate_desc,
835 new_game,
836 dup_game,
837 free_game,
838 FALSE, solve_game,
839 FALSE, game_can_format_as_text_now, game_text_format,
840 new_ui,
841 free_ui,
842 encode_ui,
843 decode_ui,
844 game_changed_state,
845 interpret_move,
846 execute_move,
847 20 /* FIXME */, game_compute_size, game_set_size,
848 game_colours,
849 game_new_drawstate,
850 game_free_drawstate,
851 game_redraw,
852 game_anim_length,
853 game_flash_length,
854 game_status,
855 FALSE, FALSE, game_print_size, game_print,
856 FALSE, /* wants_statusbar */
857 FALSE, game_timing_state,
858 0, /* flags */
859};
diff --git a/apps/plugins/puzzles/src/unfinished/slide.R b/apps/plugins/puzzles/src/unfinished/slide.R
new file mode 100644
index 0000000000..189ed652d1
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/slide.R
@@ -0,0 +1,24 @@
1# -*- makefile -*-
2
3SLIDE_EXTRA = dsf tree234
4
5slide : [X] GTK COMMON slide SLIDE_EXTRA slide-icon|no-icon
6
7slide : [G] WINDOWS COMMON slide SLIDE_EXTRA slide.res|noicon.res
8
9slidesolver : [U] slide[STANDALONE_SOLVER] SLIDE_EXTRA STANDALONE
10slidesolver : [C] slide[STANDALONE_SOLVER] SLIDE_EXTRA STANDALONE
11
12ALL += slide[COMBINED] SLIDE_EXTRA
13
14!begin am gtk
15GAMES += slide
16!end
17
18!begin >list.c
19 A(slide) \
20!end
21
22!begin >gamedesc.txt
23slide:slide.exe:Slide:Sliding block puzzle:Slide the blocks to let the key block out.
24!end
diff --git a/apps/plugins/puzzles/src/unfinished/slide.c b/apps/plugins/puzzles/src/unfinished/slide.c
new file mode 100644
index 0000000000..9d4fce1461
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/slide.c
@@ -0,0 +1,2445 @@
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#include <math.h>
36
37#include "puzzles.h"
38#include "tree234.h"
39
40/*
41 * The implementation of this game revolves around the insight
42 * which makes an exhaustive-search solver feasible: although
43 * there are many blocks which can be rearranged in many ways, any
44 * two blocks of the same shape are _indistinguishable_ and hence
45 * the number of _distinct_ board layouts is generally much
46 * smaller. So we adopt a representation for board layouts which
47 * is inherently canonical, i.e. there are no two distinct
48 * representations which encode indistinguishable layouts.
49 *
50 * The way we do this is to encode each square of the board, in
51 * the normal left-to-right top-to-bottom order, as being one of
52 * the following things:
53 * - the first square (in the given order) of a block (`anchor')
54 * - special case of the above: the anchor for the _main_ block
55 * (i.e. the one which the aim of the game is to get to the
56 * target position)
57 * - a subsequent square of a block whose previous square was N
58 * squares ago
59 * - an impassable wall
60 *
61 * (We also separately store data about which board positions are
62 * forcefields only passable by the main block. We can't encode
63 * that in the main board data, because then the main block would
64 * destroy forcefields as it went over them.)
65 *
66 * Hence, for example, a 2x2 square block would be encoded as
67 * ANCHOR, followed by DIST(1), and w-2 squares later on there
68 * would be DIST(w-1) followed by DIST(1). So if you start at the
69 * last of those squares, the DIST numbers give you a linked list
70 * pointing back through all the other squares in the same block.
71 *
72 * So the solver simply does a bfs over all reachable positions,
73 * encoding them in this format and storing them in a tree234 to
74 * ensure it doesn't ever revisit an already-analysed position.
75 */
76
77enum {
78 /*
79 * The colours are arranged here so that every base colour is
80 * directly followed by its highlight colour and then its
81 * lowlight colour. Do not break this, or draw_tile() will get
82 * confused.
83 */
84 COL_BACKGROUND,
85 COL_HIGHLIGHT,
86 COL_LOWLIGHT,
87 COL_DRAGGING,
88 COL_DRAGGING_HIGHLIGHT,
89 COL_DRAGGING_LOWLIGHT,
90 COL_MAIN,
91 COL_MAIN_HIGHLIGHT,
92 COL_MAIN_LOWLIGHT,
93 COL_MAIN_DRAGGING,
94 COL_MAIN_DRAGGING_HIGHLIGHT,
95 COL_MAIN_DRAGGING_LOWLIGHT,
96 COL_TARGET,
97 COL_TARGET_HIGHLIGHT,
98 COL_TARGET_LOWLIGHT,
99 NCOLOURS
100};
101
102/*
103 * Board layout is a simple array of bytes. Each byte holds:
104 */
105#define ANCHOR 255 /* top-left-most square of some piece */
106#define MAINANCHOR 254 /* anchor of _main_ piece */
107#define EMPTY 253 /* empty square */
108#define WALL 252 /* immovable wall */
109#define MAXDIST 251
110/* all other values indicate distance back to previous square of same block */
111#define ISDIST(x) ( (unsigned char)((x)-1) <= MAXDIST-1 )
112#define DIST(x) (x)
113#define ISANCHOR(x) ( (x)==ANCHOR || (x)==MAINANCHOR )
114#define ISBLOCK(x) ( ISANCHOR(x) || ISDIST(x) )
115
116/*
117 * MAXDIST is the largest DIST value we can encode. This must
118 * therefore also be the maximum puzzle width in theory (although
119 * solver running time will dictate a much smaller limit in
120 * practice).
121 */
122#define MAXWID MAXDIST
123
124struct game_params {
125 int w, h;
126 int maxmoves;
127};
128
129struct game_immutable_state {
130 int refcount;
131 unsigned char *forcefield;
132};
133
134struct game_solution {
135 int nmoves;
136 int *moves; /* just like from solve_board() */
137 int refcount;
138};
139
140struct game_state {
141 int w, h;
142 unsigned char *board;
143 int tx, ty; /* target coords for MAINANCHOR */
144 int minmoves; /* for display only */
145 int lastmoved, lastmoved_pos; /* for move counting */
146 int movecount;
147 int completed;
148 int cheated;
149 struct game_immutable_state *imm;
150 struct game_solution *soln;
151 int soln_index;
152};
153
154static game_params *default_params(void)
155{
156 game_params *ret = snew(game_params);
157
158 ret->w = 7;
159 ret->h = 6;
160 ret->maxmoves = 40;
161
162 return ret;
163}
164
165static const struct game_params slide_presets[] = {
166 {7, 6, 25},
167 {7, 6, -1},
168 {8, 6, -1},
169};
170
171static int game_fetch_preset(int i, char **name, game_params **params)
172{
173 game_params *ret;
174 char str[80];
175
176 if (i < 0 || i >= lenof(slide_presets))
177 return FALSE;
178
179 ret = snew(game_params);
180 *ret = slide_presets[i];
181
182 sprintf(str, "%dx%d", ret->w, ret->h);
183 if (ret->maxmoves >= 0)
184 sprintf(str + strlen(str), ", max %d moves", ret->maxmoves);
185 else
186 sprintf(str + strlen(str), ", no move limit");
187
188 *name = dupstr(str);
189 *params = ret;
190 return TRUE;
191}
192
193static void free_params(game_params *params)
194{
195 sfree(params);
196}
197
198static game_params *dup_params(const game_params *params)
199{
200 game_params *ret = snew(game_params);
201 *ret = *params; /* structure copy */
202 return ret;
203}
204
205static void decode_params(game_params *params, char const *string)
206{
207 params->w = params->h = atoi(string);
208 while (*string && isdigit((unsigned char)*string)) string++;
209 if (*string == 'x') {
210 string++;
211 params->h = atoi(string);
212 while (*string && isdigit((unsigned char)*string)) string++;
213 }
214 if (*string == 'm') {
215 string++;
216 params->maxmoves = atoi(string);
217 while (*string && isdigit((unsigned char)*string)) string++;
218 } else if (*string == 'u') {
219 string++;
220 params->maxmoves = -1;
221 }
222}
223
224static char *encode_params(const game_params *params, int full)
225{
226 char data[256];
227
228 sprintf(data, "%dx%d", params->w, params->h);
229 if (params->maxmoves >= 0)
230 sprintf(data + strlen(data), "m%d", params->maxmoves);
231 else
232 sprintf(data + strlen(data), "u");
233
234 return dupstr(data);
235}
236
237static config_item *game_configure(const game_params *params)
238{
239 config_item *ret;
240 char buf[80];
241
242 ret = snewn(4, config_item);
243
244 ret[0].name = "Width";
245 ret[0].type = C_STRING;
246 sprintf(buf, "%d", params->w);
247 ret[0].sval = dupstr(buf);
248 ret[0].ival = 0;
249
250 ret[1].name = "Height";
251 ret[1].type = C_STRING;
252 sprintf(buf, "%d", params->h);
253 ret[1].sval = dupstr(buf);
254 ret[1].ival = 0;
255
256 ret[2].name = "Solution length limit";
257 ret[2].type = C_STRING;
258 sprintf(buf, "%d", params->maxmoves);
259 ret[2].sval = dupstr(buf);
260 ret[2].ival = 0;
261
262 ret[3].name = NULL;
263 ret[3].type = C_END;
264 ret[3].sval = NULL;
265 ret[3].ival = 0;
266
267 return ret;
268}
269
270static game_params *custom_params(const config_item *cfg)
271{
272 game_params *ret = snew(game_params);
273
274 ret->w = atoi(cfg[0].sval);
275 ret->h = atoi(cfg[1].sval);
276 ret->maxmoves = atoi(cfg[2].sval);
277
278 return ret;
279}
280
281static char *validate_params(const game_params *params, int full)
282{
283 if (params->w > MAXWID)
284 return "Width must be at most " STR(MAXWID);
285
286 if (params->w < 5)
287 return "Width must be at least 5";
288 if (params->h < 4)
289 return "Height must be at least 4";
290
291 return NULL;
292}
293
294static char *board_text_format(int w, int h, unsigned char *data,
295 unsigned char *forcefield)
296{
297 int wh = w*h;
298 int *dsf = snew_dsf(wh);
299 int i, x, y;
300 int retpos, retlen = (w*2+2)*(h*2+1)+1;
301 char *ret = snewn(retlen, char);
302
303 for (i = 0; i < wh; i++)
304 if (ISDIST(data[i]))
305 dsf_merge(dsf, i - data[i], i);
306 retpos = 0;
307 for (y = 0; y < 2*h+1; y++) {
308 for (x = 0; x < 2*w+1; x++) {
309 int v;
310 int i = (y/2)*w+(x/2);
311
312#define dtype(i) (ISBLOCK(data[i]) ? \
313 dsf_canonify(dsf, i) : data[i])
314#define dchar(t) ((t)==EMPTY ? ' ' : (t)==WALL ? '#' : \
315 data[t] == MAINANCHOR ? '*' : '%')
316
317 if (y % 2 && x % 2) {
318 int j = dtype(i);
319 v = dchar(j);
320 } else if (y % 2 && !(x % 2)) {
321 int j1 = (x > 0 ? dtype(i-1) : -1);
322 int j2 = (x < 2*w ? dtype(i) : -1);
323 if (j1 != j2)
324 v = '|';
325 else
326 v = dchar(j1);
327 } else if (!(y % 2) && (x % 2)) {
328 int j1 = (y > 0 ? dtype(i-w) : -1);
329 int j2 = (y < 2*h ? dtype(i) : -1);
330 if (j1 != j2)
331 v = '-';
332 else
333 v = dchar(j1);
334 } else {
335 int j1 = (x > 0 && y > 0 ? dtype(i-w-1) : -1);
336 int j2 = (x > 0 && y < 2*h ? dtype(i-1) : -1);
337 int j3 = (x < 2*w && y > 0 ? dtype(i-w) : -1);
338 int j4 = (x < 2*w && y < 2*h ? dtype(i) : -1);
339 if (j1 == j2 && j2 == j3 && j3 == j4)
340 v = dchar(j1);
341 else if (j1 == j2 && j3 == j4)
342 v = '|';
343 else if (j1 == j3 && j2 == j4)
344 v = '-';
345 else
346 v = '+';
347 }
348
349 assert(retpos < retlen);
350 ret[retpos++] = v;
351 }
352 assert(retpos < retlen);
353 ret[retpos++] = '\n';
354 }
355 assert(retpos < retlen);
356 ret[retpos++] = '\0';
357 assert(retpos == retlen);
358
359 return ret;
360}
361
362/* ----------------------------------------------------------------------
363 * Solver.
364 */
365
366/*
367 * During solver execution, the set of visited board positions is
368 * stored as a tree234 of the following structures. `w', `h' and
369 * `data' are obvious in meaning; `dist' represents the minimum
370 * distance to reach this position from the starting point.
371 *
372 * `prev' links each board to the board position from which it was
373 * most efficiently derived.
374 */
375struct board {
376 int w, h;
377 int dist;
378 struct board *prev;
379 unsigned char *data;
380};
381
382static int boardcmp(void *av, void *bv)
383{
384 struct board *a = (struct board *)av;
385 struct board *b = (struct board *)bv;
386 return memcmp(a->data, b->data, a->w * a->h);
387}
388
389static struct board *newboard(int w, int h, unsigned char *data)
390{
391 struct board *b = malloc(sizeof(struct board) + w*h);
392 b->data = (unsigned char *)b + sizeof(struct board);
393 memcpy(b->data, data, w*h);
394 b->w = w;
395 b->h = h;
396 b->dist = -1;
397 b->prev = NULL;
398 return b;
399}
400
401/*
402 * The actual solver. Given a board, attempt to find the minimum
403 * length of move sequence which moves MAINANCHOR to (tx,ty), or
404 * -1 if no solution exists. Returns that minimum length.
405 *
406 * Also, if `moveout' is provided, writes out the moves in the
407 * form of a sequence of pairs of integers indicating the source
408 * and destination points of the anchor of the moved piece in each
409 * move. Exactly twice as many integers are written as the number
410 * returned from solve_board(), and `moveout' receives an int *
411 * which is a pointer to a dynamically allocated array.
412 */
413static int solve_board(int w, int h, unsigned char *board,
414 unsigned char *forcefield, int tx, int ty,
415 int movelimit, int **moveout)
416{
417 int wh = w*h;
418 struct board *b, *b2, *b3;
419 int *next, *anchors, *which;
420 int *movereached, *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, int);
462 which = snewn(wh, int);
463 movereached = snewn(wh, int);
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
643static void generate_board(int w, int h, int *rtx, int *rty, int *minmoves,
644 random_state *rs, unsigned char **rboard,
645 unsigned char **rforcefield, int movelimit)
646{
647 int wh = w*h;
648 unsigned char *board, *board2, *forcefield;
649 unsigned char *tried_merge;
650 int *dsf;
651 int *list, nlist, pos;
652 int tx, ty;
653 int i, j;
654 int moves = 0; /* placate optimiser */
655
656 /*
657 * Set up a board and fill it with singletons, except for a
658 * border of walls.
659 */
660 board = snewn(wh, unsigned char);
661 forcefield = snewn(wh, unsigned char);
662 board2 = snewn(wh, unsigned char);
663 memset(board, ANCHOR, wh);
664 memset(forcefield, FALSE, wh);
665 for (i = 0; i < w; i++)
666 board[i] = board[i+w*(h-1)] = WALL;
667 for (i = 0; i < h; i++)
668 board[i*w] = board[i*w+(w-1)] = WALL;
669
670 tried_merge = snewn(wh * wh, unsigned char);
671 memset(tried_merge, 0, wh*wh);
672 dsf = snew_dsf(wh);
673
674 /*
675 * Invent a main piece at one extreme. (FIXME: vary the
676 * extreme, and the piece.)
677 */
678 board[w+1] = MAINANCHOR;
679 board[w+2] = DIST(1);
680 board[w*2+1] = DIST(w-1);
681 board[w*2+2] = DIST(1);
682
683 /*
684 * Invent a target position. (FIXME: vary this too.)
685 */
686 tx = w-2;
687 ty = h-3;
688 forcefield[ty*w+tx+1] = forcefield[(ty+1)*w+tx+1] = TRUE;
689 board[ty*w+tx+1] = board[(ty+1)*w+tx+1] = EMPTY;
690
691 /*
692 * Gradually remove singletons until the game becomes soluble.
693 */
694 for (j = w; j-- > 0 ;)
695 for (i = h; i-- > 0 ;)
696 if (board[i*w+j] == ANCHOR) {
697 /*
698 * See if the board is already soluble.
699 */
700 if ((moves = solve_board(w, h, board, forcefield,
701 tx, ty, movelimit, NULL)) >= 0)
702 goto soluble;
703
704 /*
705 * Otherwise, remove this piece.
706 */
707 board[i*w+j] = EMPTY;
708 }
709 assert(!"We shouldn't get here");
710 soluble:
711
712 /*
713 * Make a list of all the inter-block edges on the board.
714 */
715 list = snewn(wh*2, int);
716 nlist = 0;
717 for (i = 0; i+1 < w; i++)
718 for (j = 0; j < h; j++)
719 list[nlist++] = (j*w+i) * 2 + 0; /* edge to the right of j*w+i */
720 for (j = 0; j+1 < h; j++)
721 for (i = 0; i < w; i++)
722 list[nlist++] = (j*w+i) * 2 + 1; /* edge below j*w+i */
723
724 /*
725 * Now go through that list in random order, trying to merge
726 * the blocks on each side of each edge.
727 */
728 shuffle(list, nlist, sizeof(*list), rs);
729 while (nlist > 0) {
730 int x1, y1, p1, c1;
731 int x2, y2, p2, c2;
732
733 pos = list[--nlist];
734 y1 = y2 = pos / (w*2);
735 x1 = x2 = (pos / 2) % w;
736 if (pos % 2)
737 y2++;
738 else
739 x2++;
740 p1 = y1*w+x1;
741 p2 = y2*w+x2;
742
743 /*
744 * Immediately abandon the attempt if we've already tried
745 * to merge the same pair of blocks along a different
746 * edge.
747 */
748 c1 = dsf_canonify(dsf, p1);
749 c2 = dsf_canonify(dsf, p2);
750 if (tried_merge[c1 * wh + c2])
751 continue;
752
753 /*
754 * In order to be mergeable, these two squares must each
755 * either be, or belong to, a non-main anchor, and their
756 * anchors must also be distinct.
757 */
758 if (!ISBLOCK(board[p1]) || !ISBLOCK(board[p2]))
759 continue;
760 while (ISDIST(board[p1]))
761 p1 -= board[p1];
762 while (ISDIST(board[p2]))
763 p2 -= board[p2];
764 if (board[p1] == MAINANCHOR || board[p2] == MAINANCHOR || p1 == p2)
765 continue;
766
767 /*
768 * We can merge these blocks. Try it, and see if the
769 * puzzle remains soluble.
770 */
771 memcpy(board2, board, wh);
772 j = -1;
773 while (p1 < wh || p2 < wh) {
774 /*
775 * p1 and p2 are the squares at the head of each block
776 * list. Pick the smaller one and put it on the output
777 * block list.
778 */
779 i = min(p1, p2);
780 if (j < 0) {
781 board[i] = ANCHOR;
782 } else {
783 assert(i - j <= MAXDIST);
784 board[i] = DIST(i - j);
785 }
786 j = i;
787
788 /*
789 * Now advance whichever list that came from.
790 */
791 if (i == p1) {
792 do {
793 p1++;
794 } while (p1 < wh && board[p1] != DIST(p1-i));
795 } else {
796 do {
797 p2++;
798 } while (p2 < wh && board[p2] != DIST(p2-i));
799 }
800 }
801 j = solve_board(w, h, board, forcefield, tx, ty, movelimit, NULL);
802 if (j < 0) {
803 /*
804 * Didn't work. Revert the merge.
805 */
806 memcpy(board, board2, wh);
807 tried_merge[c1 * wh + c2] = tried_merge[c2 * wh + c1] = TRUE;
808 } else {
809 int c;
810
811 moves = j;
812
813 dsf_merge(dsf, c1, c2);
814 c = dsf_canonify(dsf, c1);
815 for (i = 0; i < wh; i++)
816 tried_merge[c*wh+i] = (tried_merge[c1*wh+i] |
817 tried_merge[c2*wh+i]);
818 for (i = 0; i < wh; i++)
819 tried_merge[i*wh+c] = (tried_merge[i*wh+c1] |
820 tried_merge[i*wh+c2]);
821 }
822 }
823
824 sfree(dsf);
825 sfree(list);
826 sfree(tried_merge);
827 sfree(board2);
828
829 *rtx = tx;
830 *rty = ty;
831 *rboard = board;
832 *rforcefield = forcefield;
833 *minmoves = moves;
834}
835
836/* ----------------------------------------------------------------------
837 * End of solver/generator code.
838 */
839
840static char *new_game_desc(const game_params *params, random_state *rs,
841 char **aux, int interactive)
842{
843 int w = params->w, h = params->h, wh = w*h;
844 int tx, ty, minmoves;
845 unsigned char *board, *forcefield;
846 char *ret, *p;
847 int i;
848
849 generate_board(params->w, params->h, &tx, &ty, &minmoves, rs,
850 &board, &forcefield, params->maxmoves);
851#ifdef GENERATOR_DIAGNOSTICS
852 {
853 char *t = board_text_format(params->w, params->h, board);
854 printf("%s\n", t);
855 sfree(t);
856 }
857#endif
858
859 /*
860 * Encode as a game ID.
861 */
862 ret = snewn(wh * 6 + 40, char);
863 p = ret;
864 i = 0;
865 while (i < wh) {
866 if (ISDIST(board[i])) {
867 p += sprintf(p, "d%d", board[i]);
868 i++;
869 } else {
870 int count = 1;
871 int b = board[i], f = forcefield[i];
872 int c = (b == ANCHOR ? 'a' :
873 b == MAINANCHOR ? 'm' :
874 b == EMPTY ? 'e' :
875 /* b == WALL ? */ 'w');
876 if (f) *p++ = 'f';
877 *p++ = c;
878 i++;
879 while (i < wh && board[i] == b && forcefield[i] == f)
880 i++, count++;
881 if (count > 1)
882 p += sprintf(p, "%d", count);
883 }
884 }
885 p += sprintf(p, ",%d,%d,%d", tx, ty, minmoves);
886 ret = sresize(ret, p+1 - ret, char);
887
888 sfree(board);
889 sfree(forcefield);
890
891 return ret;
892}
893
894static char *validate_desc(const game_params *params, const char *desc)
895{
896 int w = params->w, h = params->h, wh = w*h;
897 int *active, *link;
898 int mains = 0;
899 int i, tx, ty, minmoves;
900 char *ret;
901
902 active = snewn(wh, int);
903 link = snewn(wh, int);
904 i = 0;
905
906 while (*desc && *desc != ',') {
907 if (i >= wh) {
908 ret = "Too much data in game description";
909 goto done;
910 }
911 link[i] = -1;
912 active[i] = FALSE;
913 if (*desc == 'f' || *desc == 'F') {
914 desc++;
915 if (!*desc) {
916 ret = "Expected another character after 'f' in game "
917 "description";
918 goto done;
919 }
920 }
921
922 if (*desc == 'd' || *desc == 'D') {
923 int dist;
924
925 desc++;
926 if (!isdigit((unsigned char)*desc)) {
927 ret = "Expected a number after 'd' in game description";
928 goto done;
929 }
930 dist = atoi(desc);
931 while (*desc && isdigit((unsigned char)*desc)) desc++;
932
933 if (dist <= 0 || dist > i) {
934 ret = "Out-of-range number after 'd' in game description";
935 goto done;
936 }
937
938 if (!active[i - dist]) {
939 ret = "Invalid back-reference in game description";
940 goto done;
941 }
942
943 link[i] = i - dist;
944
945 active[i] = TRUE;
946 active[link[i]] = FALSE;
947 i++;
948 } else {
949 int c = *desc++;
950 int count = 1;
951
952 if (!strchr("aAmMeEwW", c)) {
953 ret = "Invalid character in game description";
954 goto done;
955 }
956 if (isdigit((unsigned char)*desc)) {
957 count = atoi(desc);
958 while (*desc && isdigit((unsigned char)*desc)) desc++;
959 }
960 if (i + count > wh) {
961 ret = "Too much data in game description";
962 goto done;
963 }
964 while (count-- > 0) {
965 active[i] = (strchr("aAmM", c) != NULL);
966 link[i] = -1;
967 if (strchr("mM", c) != NULL) {
968 mains++;
969 }
970 i++;
971 }
972 }
973 }
974 if (mains != 1) {
975 ret = (mains == 0 ? "No main piece specified in game description" :
976 "More than one main piece specified in game description");
977 goto done;
978 }
979 if (i < wh) {
980 ret = "Not enough data in game description";
981 goto done;
982 }
983
984 /*
985 * Now read the target coordinates.
986 */
987 i = sscanf(desc, ",%d,%d,%d", &tx, &ty, &minmoves);
988 if (i < 2) {
989 ret = "No target coordinates specified";
990 goto done;
991 /*
992 * (but minmoves is optional)
993 */
994 }
995
996 ret = NULL;
997
998 done:
999 sfree(active);
1000 sfree(link);
1001 return ret;
1002}
1003
1004static game_state *new_game(midend *me, const game_params *params,
1005 const char *desc)
1006{
1007 int w = params->w, h = params->h, wh = w*h;
1008 game_state *state;
1009 int i;
1010
1011 state = snew(game_state);
1012 state->w = w;
1013 state->h = h;
1014 state->board = snewn(wh, unsigned char);
1015 state->lastmoved = state->lastmoved_pos = -1;
1016 state->movecount = 0;
1017 state->imm = snew(struct game_immutable_state);
1018 state->imm->refcount = 1;
1019 state->imm->forcefield = snewn(wh, unsigned char);
1020
1021 i = 0;
1022
1023 while (*desc && *desc != ',') {
1024 int f = FALSE;
1025
1026 assert(i < wh);
1027
1028 if (*desc == 'f') {
1029 f = TRUE;
1030 desc++;
1031 assert(*desc);
1032 }
1033
1034 if (*desc == 'd' || *desc == 'D') {
1035 int dist;
1036
1037 desc++;
1038 dist = atoi(desc);
1039 while (*desc && isdigit((unsigned char)*desc)) desc++;
1040
1041 state->board[i] = DIST(dist);
1042 state->imm->forcefield[i] = f;
1043
1044 i++;
1045 } else {
1046 int c = *desc++;
1047 int count = 1;
1048
1049 if (isdigit((unsigned char)*desc)) {
1050 count = atoi(desc);
1051 while (*desc && isdigit((unsigned char)*desc)) desc++;
1052 }
1053 assert(i + count <= wh);
1054
1055 c = (c == 'a' || c == 'A' ? ANCHOR :
1056 c == 'm' || c == 'M' ? MAINANCHOR :
1057 c == 'e' || c == 'E' ? EMPTY :
1058 /* c == 'w' || c == 'W' ? */ WALL);
1059
1060 while (count-- > 0) {
1061 state->board[i] = c;
1062 state->imm->forcefield[i] = f;
1063 i++;
1064 }
1065 }
1066 }
1067
1068 /*
1069 * Now read the target coordinates.
1070 */
1071 state->tx = state->ty = 0;
1072 state->minmoves = -1;
1073 i = sscanf(desc, ",%d,%d,%d", &state->tx, &state->ty, &state->minmoves);
1074
1075 if (state->board[state->ty*w+state->tx] == MAINANCHOR)
1076 state->completed = 0; /* already complete! */
1077 else
1078 state->completed = -1;
1079
1080 state->cheated = FALSE;
1081 state->soln = NULL;
1082 state->soln_index = -1;
1083
1084 return state;
1085}
1086
1087static game_state *dup_game(const game_state *state)
1088{
1089 int w = state->w, h = state->h, wh = w*h;
1090 game_state *ret = snew(game_state);
1091
1092 ret->w = state->w;
1093 ret->h = state->h;
1094 ret->board = snewn(wh, unsigned char);
1095 memcpy(ret->board, state->board, wh);
1096 ret->tx = state->tx;
1097 ret->ty = state->ty;
1098 ret->minmoves = state->minmoves;
1099 ret->lastmoved = state->lastmoved;
1100 ret->lastmoved_pos = state->lastmoved_pos;
1101 ret->movecount = state->movecount;
1102 ret->completed = state->completed;
1103 ret->cheated = state->cheated;
1104 ret->imm = state->imm;
1105 ret->imm->refcount++;
1106 ret->soln = state->soln;
1107 ret->soln_index = state->soln_index;
1108 if (ret->soln)
1109 ret->soln->refcount++;
1110
1111 return ret;
1112}
1113
1114static void free_game(game_state *state)
1115{
1116 if (--state->imm->refcount <= 0) {
1117 sfree(state->imm->forcefield);
1118 sfree(state->imm);
1119 }
1120 if (state->soln && --state->soln->refcount <= 0) {
1121 sfree(state->soln->moves);
1122 sfree(state->soln);
1123 }
1124 sfree(state->board);
1125 sfree(state);
1126}
1127
1128static char *solve_game(const game_state *state, const game_state *currstate,
1129 const char *aux, char **error)
1130{
1131 int *moves;
1132 int nmoves;
1133 int i;
1134 char *ret, *p, sep;
1135
1136 /*
1137 * Run the solver and attempt to find the shortest solution
1138 * from the current position.
1139 */
1140 nmoves = solve_board(state->w, state->h, state->board,
1141 state->imm->forcefield, state->tx, state->ty,
1142 -1, &moves);
1143
1144 if (nmoves < 0) {
1145 *error = "Unable to find a solution to this puzzle";
1146 return NULL;
1147 }
1148 if (nmoves == 0) {
1149 *error = "Puzzle is already solved";
1150 return NULL;
1151 }
1152
1153 /*
1154 * Encode the resulting solution as a move string.
1155 */
1156 ret = snewn(nmoves * 40, char);
1157 p = ret;
1158 sep = 'S';
1159
1160 for (i = 0; i < nmoves; i++) {
1161 p += sprintf(p, "%c%d-%d", sep, moves[i*2], moves[i*2+1]);
1162 sep = ',';
1163 }
1164
1165 sfree(moves);
1166 assert(p - ret < nmoves * 40);
1167 ret = sresize(ret, p+1 - ret, char);
1168
1169 return ret;
1170}
1171
1172static int game_can_format_as_text_now(const game_params *params)
1173{
1174 return TRUE;
1175}
1176
1177static char *game_text_format(const game_state *state)
1178{
1179 return board_text_format(state->w, state->h, state->board,
1180 state->imm->forcefield);
1181}
1182
1183struct game_ui {
1184 int dragging;
1185 int drag_anchor;
1186 int drag_offset_x, drag_offset_y;
1187 int drag_currpos;
1188 unsigned char *reachable;
1189 int *bfs_queue; /* used as scratch in interpret_move */
1190};
1191
1192static game_ui *new_ui(const game_state *state)
1193{
1194 int w = state->w, h = state->h, wh = w*h;
1195 game_ui *ui = snew(game_ui);
1196
1197 ui->dragging = FALSE;
1198 ui->drag_anchor = ui->drag_currpos = -1;
1199 ui->drag_offset_x = ui->drag_offset_y = -1;
1200 ui->reachable = snewn(wh, unsigned char);
1201 memset(ui->reachable, 0, wh);
1202 ui->bfs_queue = snewn(wh, int);
1203
1204 return ui;
1205}
1206
1207static void free_ui(game_ui *ui)
1208{
1209 sfree(ui->bfs_queue);
1210 sfree(ui->reachable);
1211 sfree(ui);
1212}
1213
1214static char *encode_ui(const game_ui *ui)
1215{
1216 return NULL;
1217}
1218
1219static void decode_ui(game_ui *ui, const char *encoding)
1220{
1221}
1222
1223static void game_changed_state(game_ui *ui, const game_state *oldstate,
1224 const game_state *newstate)
1225{
1226}
1227
1228#define PREFERRED_TILESIZE 32
1229#define TILESIZE (ds->tilesize)
1230#define BORDER (TILESIZE/2)
1231#define COORD(x) ( (x) * TILESIZE + BORDER )
1232#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
1233#define BORDER_WIDTH (1 + TILESIZE/20)
1234#define HIGHLIGHT_WIDTH (1 + TILESIZE/16)
1235
1236#define FLASH_INTERVAL 0.10F
1237#define FLASH_TIME 3*FLASH_INTERVAL
1238
1239struct game_drawstate {
1240 int tilesize;
1241 int w, h;
1242 unsigned long *grid; /* what's currently displayed */
1243 int started;
1244};
1245
1246static char *interpret_move(const game_state *state, game_ui *ui,
1247 const game_drawstate *ds,
1248 int x, int y, int button)
1249{
1250 int w = state->w, h = state->h, wh = w*h;
1251 int tx, ty, i, j;
1252 int qhead, qtail;
1253
1254 if (button == LEFT_BUTTON) {
1255 tx = FROMCOORD(x);
1256 ty = FROMCOORD(y);
1257
1258 if (tx < 0 || tx >= w || ty < 0 || ty >= h ||
1259 !ISBLOCK(state->board[ty*w+tx]))
1260 return NULL; /* this click has no effect */
1261
1262 /*
1263 * User has clicked on a block. Find the block's anchor
1264 * and register that we've started dragging it.
1265 */
1266 i = ty*w+tx;
1267 while (ISDIST(state->board[i]))
1268 i -= state->board[i];
1269 assert(i >= 0 && i < wh);
1270
1271 ui->dragging = TRUE;
1272 ui->drag_anchor = i;
1273 ui->drag_offset_x = tx - (i % w);
1274 ui->drag_offset_y = ty - (i / w);
1275 ui->drag_currpos = i;
1276
1277 /*
1278 * Now we immediately bfs out from the current location of
1279 * the anchor, to find all the places to which this block
1280 * can be dragged.
1281 */
1282 memset(ui->reachable, FALSE, wh);
1283 qhead = qtail = 0;
1284 ui->reachable[i] = TRUE;
1285 ui->bfs_queue[qtail++] = i;
1286 for (j = i; j < wh; j++)
1287 if (state->board[j] == DIST(j - i))
1288 i = j;
1289 while (qhead < qtail) {
1290 int pos = ui->bfs_queue[qhead++];
1291 int x = pos % w, y = pos / w;
1292 int dir;
1293
1294 for (dir = 0; dir < 4; dir++) {
1295 int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0);
1296 int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0);
1297 int newpos;
1298
1299 if (x + dx < 0 || x + dx >= w ||
1300 y + dy < 0 || y + dy >= h)
1301 continue;
1302
1303 newpos = pos + dy*w + dx;
1304 if (ui->reachable[newpos])
1305 continue; /* already done this one */
1306
1307 /*
1308 * Now search the grid to see if the block we're
1309 * dragging could fit into this space.
1310 */
1311 for (j = i; j >= 0; j = (ISDIST(state->board[j]) ?
1312 j - state->board[j] : -1)) {
1313 int jx = (j+pos-ui->drag_anchor) % w;
1314 int jy = (j+pos-ui->drag_anchor) / w;
1315 int j2;
1316
1317 if (jx + dx < 0 || jx + dx >= w ||
1318 jy + dy < 0 || jy + dy >= h)
1319 break; /* this position isn't valid at all */
1320
1321 j2 = (j+pos-ui->drag_anchor) + dy*w + dx;
1322
1323 if (state->board[j2] == EMPTY &&
1324 (!state->imm->forcefield[j2] ||
1325 state->board[ui->drag_anchor] == MAINANCHOR))
1326 continue;
1327 while (ISDIST(state->board[j2]))
1328 j2 -= state->board[j2];
1329 assert(j2 >= 0 && j2 < wh);
1330 if (j2 == ui->drag_anchor)
1331 continue;
1332 else
1333 break;
1334 }
1335
1336 if (j < 0) {
1337 /*
1338 * If we got to the end of that loop without
1339 * disqualifying this position, mark it as
1340 * reachable for this drag.
1341 */
1342 ui->reachable[newpos] = TRUE;
1343 ui->bfs_queue[qtail++] = newpos;
1344 }
1345 }
1346 }
1347
1348 /*
1349 * And that's it. Update the display to reflect the start
1350 * of a drag.
1351 */
1352 return "";
1353 } else if (button == LEFT_DRAG && ui->dragging) {
1354 int dist, distlimit, dx, dy, s, px, py;
1355
1356 tx = FROMCOORD(x);
1357 ty = FROMCOORD(y);
1358
1359 tx -= ui->drag_offset_x;
1360 ty -= ui->drag_offset_y;
1361
1362 /*
1363 * Now search outwards from (tx,ty), in order of Manhattan
1364 * distance, until we find a reachable square.
1365 */
1366 distlimit = w+tx;
1367 distlimit = max(distlimit, h+ty);
1368 distlimit = max(distlimit, tx);
1369 distlimit = max(distlimit, ty);
1370 for (dist = 0; dist <= distlimit; dist++) {
1371 for (dx = -dist; dx <= dist; dx++)
1372 for (s = -1; s <= +1; s += 2) {
1373 dy = s * (dist - abs(dx));
1374 px = tx + dx;
1375 py = ty + dy;
1376 if (px >= 0 && px < w && py >= 0 && py < h &&
1377 ui->reachable[py*w+px]) {
1378 ui->drag_currpos = py*w+px;
1379 return "";
1380 }
1381 }
1382 }
1383 return NULL; /* give up - this drag has no effect */
1384 } else if (button == LEFT_RELEASE && ui->dragging) {
1385 char data[256], *str;
1386
1387 /*
1388 * Terminate the drag, and if the piece has actually moved
1389 * then return a move string quoting the old and new
1390 * locations of the piece's anchor.
1391 */
1392 if (ui->drag_anchor != ui->drag_currpos) {
1393 sprintf(data, "M%d-%d", ui->drag_anchor, ui->drag_currpos);
1394 str = dupstr(data);
1395 } else
1396 str = ""; /* null move; just update the UI */
1397
1398 ui->dragging = FALSE;
1399 ui->drag_anchor = ui->drag_currpos = -1;
1400 ui->drag_offset_x = ui->drag_offset_y = -1;
1401 memset(ui->reachable, 0, wh);
1402
1403 return str;
1404 } else if (button == ' ' && state->soln) {
1405 /*
1406 * Make the next move in the stored solution.
1407 */
1408 char data[256];
1409 int a1, a2;
1410
1411 a1 = state->soln->moves[state->soln_index*2];
1412 a2 = state->soln->moves[state->soln_index*2+1];
1413 if (a1 == state->lastmoved_pos)
1414 a1 = state->lastmoved;
1415
1416 sprintf(data, "M%d-%d", a1, a2);
1417 return dupstr(data);
1418 }
1419
1420 return NULL;
1421}
1422
1423static int move_piece(int w, int h, const unsigned char *src,
1424 unsigned char *dst, unsigned char *ff, int from, int to)
1425{
1426 int wh = w*h;
1427 int i, j;
1428
1429 if (!ISANCHOR(dst[from]))
1430 return FALSE;
1431
1432 /*
1433 * Scan to the far end of the piece's linked list.
1434 */
1435 for (i = j = from; j < wh; j++)
1436 if (src[j] == DIST(j - i))
1437 i = j;
1438
1439 /*
1440 * Remove the piece from its old location in the new
1441 * game state.
1442 */
1443 for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1))
1444 dst[j] = EMPTY;
1445
1446 /*
1447 * And put it back in at the new location.
1448 */
1449 for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1)) {
1450 int jn = j + to - from;
1451 if (jn < 0 || jn >= wh)
1452 return FALSE;
1453 if (dst[jn] == EMPTY && (!ff[jn] || src[from] == MAINANCHOR)) {
1454 dst[jn] = src[j];
1455 } else {
1456 return FALSE;
1457 }
1458 }
1459
1460 return TRUE;
1461}
1462
1463static game_state *execute_move(const game_state *state, const char *move)
1464{
1465 int w = state->w, h = state->h /* , wh = w*h */;
1466 char c;
1467 int a1, a2, n, movesize;
1468 game_state *ret = dup_game(state);
1469
1470 while (*move) {
1471 c = *move;
1472 if (c == 'S') {
1473 /*
1474 * This is a solve move, so we just set up a stored
1475 * solution path.
1476 */
1477 if (ret->soln && --ret->soln->refcount <= 0) {
1478 sfree(ret->soln->moves);
1479 sfree(ret->soln);
1480 }
1481 ret->soln = snew(struct game_solution);
1482 ret->soln->nmoves = 0;
1483 ret->soln->moves = NULL;
1484 ret->soln->refcount = 1;
1485 ret->soln_index = 0;
1486 ret->cheated = TRUE;
1487
1488 movesize = 0;
1489 move++;
1490 while (1) {
1491 if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2) {
1492 free_game(ret);
1493 return NULL;
1494 }
1495
1496 /*
1497 * Special case: if the first move in the solution
1498 * involves the piece for which we already have a
1499 * partial stored move, adjust the source point to
1500 * the original starting point of that piece.
1501 */
1502 if (ret->soln->nmoves == 0 && a1 == ret->lastmoved)
1503 a1 = ret->lastmoved_pos;
1504
1505 if (ret->soln->nmoves >= movesize) {
1506 movesize = (ret->soln->nmoves + 48) * 4 / 3;
1507 ret->soln->moves = sresize(ret->soln->moves,
1508 2*movesize, int);
1509 }
1510
1511 ret->soln->moves[2*ret->soln->nmoves] = a1;
1512 ret->soln->moves[2*ret->soln->nmoves+1] = a2;
1513 ret->soln->nmoves++;
1514 move += n;
1515 if (*move != ',')
1516 break;
1517 move++; /* eat comma */
1518 }
1519 } else if (c == 'M') {
1520 move++;
1521 if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2 ||
1522 !move_piece(w, h, state->board, ret->board,
1523 state->imm->forcefield, a1, a2)) {
1524 free_game(ret);
1525 return NULL;
1526 }
1527 if (a1 == ret->lastmoved) {
1528 /*
1529 * If the player has moved the same piece as they
1530 * moved last time, don't increment the move
1531 * count. In fact, if they've put the piece back
1532 * where it started from, _decrement_ the move
1533 * count.
1534 */
1535 if (a2 == ret->lastmoved_pos) {
1536 ret->movecount--; /* reverted last move */
1537 ret->lastmoved = ret->lastmoved_pos = -1;
1538 } else {
1539 ret->lastmoved = a2;
1540 /* don't change lastmoved_pos */
1541 }
1542 } else {
1543 ret->lastmoved = a2;
1544 ret->lastmoved_pos = a1;
1545 ret->movecount++;
1546 }
1547
1548 /*
1549 * If we have a stored solution path, see if we've
1550 * strayed from it or successfully made the next move
1551 * along it.
1552 */
1553 if (ret->soln && ret->lastmoved_pos >= 0) {
1554 if (ret->lastmoved_pos !=
1555 ret->soln->moves[ret->soln_index*2]) {
1556 /* strayed from the path */
1557 ret->soln->refcount--;
1558 assert(ret->soln->refcount > 0);
1559 /* `state' at least still exists */
1560 ret->soln = NULL;
1561 ret->soln_index = -1;
1562 } else if (ret->lastmoved ==
1563 ret->soln->moves[ret->soln_index*2+1]) {
1564 /* advanced along the path */
1565 ret->soln_index++;
1566 if (ret->soln_index >= ret->soln->nmoves) {
1567 /* finished the path! */
1568 ret->soln->refcount--;
1569 assert(ret->soln->refcount > 0);
1570 /* `state' at least still exists */
1571 ret->soln = NULL;
1572 ret->soln_index = -1;
1573 }
1574 }
1575 }
1576
1577 if (ret->board[a2] == MAINANCHOR &&
1578 a2 == ret->ty * w + ret->tx && ret->completed < 0)
1579 ret->completed = ret->movecount;
1580 move += n;
1581 } else {
1582 free_game(ret);
1583 return NULL;
1584 }
1585 if (*move == ';')
1586 move++;
1587 else if (*move) {
1588 free_game(ret);
1589 return NULL;
1590 }
1591 }
1592
1593 return ret;
1594}
1595
1596/* ----------------------------------------------------------------------
1597 * Drawing routines.
1598 */
1599
1600static void game_compute_size(const game_params *params, int tilesize,
1601 int *x, int *y)
1602{
1603 /* fool the macros */
1604 struct dummy { int tilesize; } dummy, *ds = &dummy;
1605 dummy.tilesize = tilesize;
1606
1607 *x = params->w * TILESIZE + 2*BORDER;
1608 *y = params->h * TILESIZE + 2*BORDER;
1609}
1610
1611static void game_set_size(drawing *dr, game_drawstate *ds,
1612 const game_params *params, int tilesize)
1613{
1614 ds->tilesize = tilesize;
1615}
1616
1617static void raise_colour(float *target, float *src, float *limit)
1618{
1619 int i;
1620 for (i = 0; i < 3; i++)
1621 target[i] = (2*src[i] + limit[i]) / 3;
1622}
1623
1624static float *game_colours(frontend *fe, int *ncolours)
1625{
1626 float *ret = snewn(3 * NCOLOURS, float);
1627
1628 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1629
1630 /*
1631 * When dragging a tile, we light it up a bit.
1632 */
1633 raise_colour(ret+3*COL_DRAGGING,
1634 ret+3*COL_BACKGROUND, ret+3*COL_HIGHLIGHT);
1635 raise_colour(ret+3*COL_DRAGGING_HIGHLIGHT,
1636 ret+3*COL_HIGHLIGHT, ret+3*COL_HIGHLIGHT);
1637 raise_colour(ret+3*COL_DRAGGING_LOWLIGHT,
1638 ret+3*COL_LOWLIGHT, ret+3*COL_HIGHLIGHT);
1639
1640 /*
1641 * The main tile is tinted blue.
1642 */
1643 ret[COL_MAIN * 3 + 0] = ret[COL_BACKGROUND * 3 + 0];
1644 ret[COL_MAIN * 3 + 1] = ret[COL_BACKGROUND * 3 + 1];
1645 ret[COL_MAIN * 3 + 2] = ret[COL_HIGHLIGHT * 3 + 2];
1646 game_mkhighlight_specific(fe, ret, COL_MAIN,
1647 COL_MAIN_HIGHLIGHT, COL_MAIN_LOWLIGHT);
1648
1649 /*
1650 * And we light that up a bit too when dragging.
1651 */
1652 raise_colour(ret+3*COL_MAIN_DRAGGING,
1653 ret+3*COL_MAIN, ret+3*COL_MAIN_HIGHLIGHT);
1654 raise_colour(ret+3*COL_MAIN_DRAGGING_HIGHLIGHT,
1655 ret+3*COL_MAIN_HIGHLIGHT, ret+3*COL_MAIN_HIGHLIGHT);
1656 raise_colour(ret+3*COL_MAIN_DRAGGING_LOWLIGHT,
1657 ret+3*COL_MAIN_LOWLIGHT, ret+3*COL_MAIN_HIGHLIGHT);
1658
1659 /*
1660 * The target area on the floor is tinted green.
1661 */
1662 ret[COL_TARGET * 3 + 0] = ret[COL_BACKGROUND * 3 + 0];
1663 ret[COL_TARGET * 3 + 1] = ret[COL_HIGHLIGHT * 3 + 1];
1664 ret[COL_TARGET * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
1665 game_mkhighlight_specific(fe, ret, COL_TARGET,
1666 COL_TARGET_HIGHLIGHT, COL_TARGET_LOWLIGHT);
1667
1668 *ncolours = NCOLOURS;
1669 return ret;
1670}
1671
1672static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1673{
1674 int w = state->w, h = state->h, wh = w*h;
1675 struct game_drawstate *ds = snew(struct game_drawstate);
1676 int i;
1677
1678 ds->tilesize = 0;
1679 ds->w = w;
1680 ds->h = h;
1681 ds->started = FALSE;
1682 ds->grid = snewn(wh, unsigned long);
1683 for (i = 0; i < wh; i++)
1684 ds->grid[i] = ~(unsigned long)0;
1685
1686 return ds;
1687}
1688
1689static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1690{
1691 sfree(ds->grid);
1692 sfree(ds);
1693}
1694
1695#define BG_NORMAL 0x00000001UL
1696#define BG_TARGET 0x00000002UL
1697#define BG_FORCEFIELD 0x00000004UL
1698#define FLASH_LOW 0x00000008UL
1699#define FLASH_HIGH 0x00000010UL
1700#define FG_WALL 0x00000020UL
1701#define FG_MAIN 0x00000040UL
1702#define FG_NORMAL 0x00000080UL
1703#define FG_DRAGGING 0x00000100UL
1704#define FG_SHADOW 0x00000200UL
1705#define FG_SOLVEPIECE 0x00000400UL
1706#define FG_MAINPIECESH 11
1707#define FG_SHADOWSH 19
1708
1709#define PIECE_LBORDER 0x00000001UL
1710#define PIECE_TBORDER 0x00000002UL
1711#define PIECE_RBORDER 0x00000004UL
1712#define PIECE_BBORDER 0x00000008UL
1713#define PIECE_TLCORNER 0x00000010UL
1714#define PIECE_TRCORNER 0x00000020UL
1715#define PIECE_BLCORNER 0x00000040UL
1716#define PIECE_BRCORNER 0x00000080UL
1717#define PIECE_MASK 0x000000FFUL
1718
1719/*
1720 * Utility function.
1721 */
1722#define TYPE_MASK 0xF000
1723#define COL_MASK 0x0FFF
1724#define TYPE_RECT 0x0000
1725#define TYPE_TLCIRC 0x4000
1726#define TYPE_TRCIRC 0x5000
1727#define TYPE_BLCIRC 0x6000
1728#define TYPE_BRCIRC 0x7000
1729static void maybe_rect(drawing *dr, int x, int y, int w, int h,
1730 int coltype, int col2)
1731{
1732 int colour = coltype & COL_MASK, type = coltype & TYPE_MASK;
1733
1734 if (colour > NCOLOURS)
1735 return;
1736 if (type == TYPE_RECT) {
1737 draw_rect(dr, x, y, w, h, colour);
1738 } else {
1739 int cx, cy, r;
1740
1741 clip(dr, x, y, w, h);
1742
1743 cx = x;
1744 cy = y;
1745 r = w-1;
1746 if (type & 0x1000)
1747 cx += r;
1748 if (type & 0x2000)
1749 cy += r;
1750
1751 if (col2 == -1 || col2 == coltype) {
1752 assert(w == h);
1753 draw_circle(dr, cx, cy, r, colour, colour);
1754 } else {
1755 /*
1756 * We aim to draw a quadrant of a circle in two
1757 * different colours. We do this using Bresenham's
1758 * algorithm directly, because the Puzzles drawing API
1759 * doesn't have a draw-sector primitive.
1760 */
1761 int bx, by, bd, bd2;
1762 int xm = (type & 0x1000 ? -1 : +1);
1763 int ym = (type & 0x2000 ? -1 : +1);
1764
1765 by = r;
1766 bx = 0;
1767 bd = 0;
1768 while (by >= bx) {
1769 /*
1770 * Plot the point.
1771 */
1772 {
1773 int x1 = cx+xm*bx, y1 = cy+ym*bx;
1774 int x2, y2;
1775
1776 x2 = cx+xm*by; y2 = y1;
1777 draw_rect(dr, min(x1,x2), min(y1,y2),
1778 abs(x1-x2)+1, abs(y1-y2)+1, colour);
1779 x2 = x1; y2 = cy+ym*by;
1780 draw_rect(dr, min(x1,x2), min(y1,y2),
1781 abs(x1-x2)+1, abs(y1-y2)+1, col2);
1782 }
1783
1784 bd += 2*bx + 1;
1785 bd2 = bd - (2*by - 1);
1786 if (abs(bd2) < abs(bd)) {
1787 bd = bd2;
1788 by--;
1789 }
1790 bx++;
1791 }
1792 }
1793
1794 unclip(dr);
1795 }
1796}
1797
1798static void draw_wallpart(drawing *dr, game_drawstate *ds,
1799 int tx, int ty, unsigned long val,
1800 int cl, int cc, int ch)
1801{
1802 int coords[6];
1803
1804 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc);
1805 if (val & PIECE_LBORDER)
1806 draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, TILESIZE,
1807 ch);
1808 if (val & PIECE_RBORDER)
1809 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1810 HIGHLIGHT_WIDTH, TILESIZE, cl);
1811 if (val & PIECE_TBORDER)
1812 draw_rect(dr, tx, ty, TILESIZE, HIGHLIGHT_WIDTH, ch);
1813 if (val & PIECE_BBORDER)
1814 draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1815 TILESIZE, HIGHLIGHT_WIDTH, cl);
1816 if (!((PIECE_BBORDER | PIECE_LBORDER) &~ val)) {
1817 draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1818 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1819 clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1820 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1821 coords[0] = tx - 1;
1822 coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1823 coords[2] = tx + HIGHLIGHT_WIDTH;
1824 coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1825 coords[4] = tx - 1;
1826 coords[5] = ty + TILESIZE;
1827 draw_polygon(dr, coords, 3, ch, ch);
1828 unclip(dr);
1829 } else if (val & PIECE_BLCORNER) {
1830 draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1831 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1832 clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1833 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1834 coords[0] = tx - 1;
1835 coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1836 coords[2] = tx + HIGHLIGHT_WIDTH;
1837 coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1838 coords[4] = tx - 1;
1839 coords[5] = ty + TILESIZE;
1840 draw_polygon(dr, coords, 3, cl, cl);
1841 unclip(dr);
1842 }
1843 if (!((PIECE_TBORDER | PIECE_RBORDER) &~ val)) {
1844 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1845 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1846 clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1847 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1848 coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1849 coords[1] = ty - 1;
1850 coords[2] = tx + TILESIZE;
1851 coords[3] = ty - 1;
1852 coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1853 coords[5] = ty + HIGHLIGHT_WIDTH;
1854 draw_polygon(dr, coords, 3, ch, ch);
1855 unclip(dr);
1856 } else if (val & PIECE_TRCORNER) {
1857 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1858 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1859 clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1860 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1861 coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1862 coords[1] = ty - 1;
1863 coords[2] = tx + TILESIZE;
1864 coords[3] = ty - 1;
1865 coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1866 coords[5] = ty + HIGHLIGHT_WIDTH;
1867 draw_polygon(dr, coords, 3, cl, cl);
1868 unclip(dr);
1869 }
1870 if (val & PIECE_TLCORNER)
1871 draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1872 if (val & PIECE_BRCORNER)
1873 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH,
1874 ty+TILESIZE-HIGHLIGHT_WIDTH,
1875 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1876}
1877
1878static void draw_piecepart(drawing *dr, game_drawstate *ds,
1879 int tx, int ty, unsigned long val,
1880 int cl, int cc, int ch)
1881{
1882 int x[6], y[6];
1883
1884 /*
1885 * Drawing the blocks is hellishly fiddly. The blocks don't
1886 * stretch to the full size of the tile; there's a border
1887 * around them of size BORDER_WIDTH. Then they have bevelled
1888 * borders of size HIGHLIGHT_WIDTH, and also rounded corners.
1889 *
1890 * I tried for some time to find a clean and clever way to
1891 * figure out what needed drawing from the corner and border
1892 * flags, but in the end the cleanest way I could find was the
1893 * following. We divide the grid square into 25 parts by
1894 * ruling four horizontal and four vertical lines across it;
1895 * those lines are at BORDER_WIDTH and BORDER_WIDTH +
1896 * HIGHLIGHT_WIDTH from the top, from the bottom, from the
1897 * left and from the right. Then we carefully consider each of
1898 * the resulting 25 sections of square, and decide separately
1899 * what needs to go in it based on the flags. In complicated
1900 * cases there can be up to five possibilities affecting any
1901 * given section (no corner or border flags, just the corner
1902 * flag, one border flag, the other border flag, both border
1903 * flags). So there's a lot of very fiddly logic here and all
1904 * I could really think to do was give it my best shot and
1905 * then test it and correct all the typos. Not fun to write,
1906 * and I'm sure it isn't fun to read either, but it seems to
1907 * work.
1908 */
1909
1910 x[0] = tx;
1911 x[1] = x[0] + BORDER_WIDTH;
1912 x[2] = x[1] + HIGHLIGHT_WIDTH;
1913 x[5] = tx + TILESIZE;
1914 x[4] = x[5] - BORDER_WIDTH;
1915 x[3] = x[4] - HIGHLIGHT_WIDTH;
1916
1917 y[0] = ty;
1918 y[1] = y[0] + BORDER_WIDTH;
1919 y[2] = y[1] + HIGHLIGHT_WIDTH;
1920 y[5] = ty + TILESIZE;
1921 y[4] = y[5] - BORDER_WIDTH;
1922 y[3] = y[4] - HIGHLIGHT_WIDTH;
1923
1924#define RECT(p,q) x[p], y[q], x[(p)+1]-x[p], y[(q)+1]-y[q]
1925
1926 maybe_rect(dr, RECT(0,0),
1927 (val & (PIECE_TLCORNER | PIECE_TBORDER |
1928 PIECE_LBORDER)) ? -1 : cc, -1);
1929 maybe_rect(dr, RECT(1,0),
1930 (val & PIECE_TLCORNER) ? ch : (val & PIECE_TBORDER) ? -1 :
1931 (val & PIECE_LBORDER) ? ch : cc, -1);
1932 maybe_rect(dr, RECT(2,0),
1933 (val & PIECE_TBORDER) ? -1 : cc, -1);
1934 maybe_rect(dr, RECT(3,0),
1935 (val & PIECE_TRCORNER) ? cl : (val & PIECE_TBORDER) ? -1 :
1936 (val & PIECE_RBORDER) ? cl : cc, -1);
1937 maybe_rect(dr, RECT(4,0),
1938 (val & (PIECE_TRCORNER | PIECE_TBORDER |
1939 PIECE_RBORDER)) ? -1 : cc, -1);
1940 maybe_rect(dr, RECT(0,1),
1941 (val & PIECE_TLCORNER) ? ch : (val & PIECE_LBORDER) ? -1 :
1942 (val & PIECE_TBORDER) ? ch : cc, -1);
1943 maybe_rect(dr, RECT(1,1),
1944 (val & PIECE_TLCORNER) ? cc : -1, -1);
1945 maybe_rect(dr, RECT(1,1),
1946 (val & PIECE_TLCORNER) ? ch | TYPE_TLCIRC :
1947 !((PIECE_TBORDER | PIECE_LBORDER) &~ val) ? ch | TYPE_BRCIRC :
1948 (val & (PIECE_TBORDER | PIECE_LBORDER)) ? ch : cc, -1);
1949 maybe_rect(dr, RECT(2,1),
1950 (val & PIECE_TBORDER) ? ch : cc, -1);
1951 maybe_rect(dr, RECT(3,1),
1952 (val & PIECE_TRCORNER) ? cc : -1, -1);
1953 maybe_rect(dr, RECT(3,1),
1954 (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_TBORDER ? ch :
1955 (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_RBORDER ? cl :
1956 !((PIECE_TBORDER|PIECE_RBORDER) &~ val) ? cl | TYPE_BLCIRC :
1957 (val & PIECE_TRCORNER) ? cl | TYPE_TRCIRC :
1958 cc, ch);
1959 maybe_rect(dr, RECT(4,1),
1960 (val & PIECE_TRCORNER) ? ch : (val & PIECE_RBORDER) ? -1 :
1961 (val & PIECE_TBORDER) ? ch : cc, -1);
1962 maybe_rect(dr, RECT(0,2),
1963 (val & PIECE_LBORDER) ? -1 : cc, -1);
1964 maybe_rect(dr, RECT(1,2),
1965 (val & PIECE_LBORDER) ? ch : cc, -1);
1966 maybe_rect(dr, RECT(2,2),
1967 cc, -1);
1968 maybe_rect(dr, RECT(3,2),
1969 (val & PIECE_RBORDER) ? cl : cc, -1);
1970 maybe_rect(dr, RECT(4,2),
1971 (val & PIECE_RBORDER) ? -1 : cc, -1);
1972 maybe_rect(dr, RECT(0,3),
1973 (val & PIECE_BLCORNER) ? cl : (val & PIECE_LBORDER) ? -1 :
1974 (val & PIECE_BBORDER) ? cl : cc, -1);
1975 maybe_rect(dr, RECT(1,3),
1976 (val & PIECE_BLCORNER) ? cc : -1, -1);
1977 maybe_rect(dr, RECT(1,3),
1978 (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_BBORDER ? cl :
1979 (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_LBORDER ? ch :
1980 !((PIECE_BBORDER|PIECE_LBORDER) &~ val) ? ch | TYPE_TRCIRC :
1981 (val & PIECE_BLCORNER) ? ch | TYPE_BLCIRC :
1982 cc, cl);
1983 maybe_rect(dr, RECT(2,3),
1984 (val & PIECE_BBORDER) ? cl : cc, -1);
1985 maybe_rect(dr, RECT(3,3),
1986 (val & PIECE_BRCORNER) ? cc : -1, -1);
1987 maybe_rect(dr, RECT(3,3),
1988 (val & PIECE_BRCORNER) ? cl | TYPE_BRCIRC :
1989 !((PIECE_BBORDER | PIECE_RBORDER) &~ val) ? cl | TYPE_TLCIRC :
1990 (val & (PIECE_BBORDER | PIECE_RBORDER)) ? cl : cc, -1);
1991 maybe_rect(dr, RECT(4,3),
1992 (val & PIECE_BRCORNER) ? cl : (val & PIECE_RBORDER) ? -1 :
1993 (val & PIECE_BBORDER) ? cl : cc, -1);
1994 maybe_rect(dr, RECT(0,4),
1995 (val & (PIECE_BLCORNER | PIECE_BBORDER |
1996 PIECE_LBORDER)) ? -1 : cc, -1);
1997 maybe_rect(dr, RECT(1,4),
1998 (val & PIECE_BLCORNER) ? ch : (val & PIECE_BBORDER) ? -1 :
1999 (val & PIECE_LBORDER) ? ch : cc, -1);
2000 maybe_rect(dr, RECT(2,4),
2001 (val & PIECE_BBORDER) ? -1 : cc, -1);
2002 maybe_rect(dr, RECT(3,4),
2003 (val & PIECE_BRCORNER) ? cl : (val & PIECE_BBORDER) ? -1 :
2004 (val & PIECE_RBORDER) ? cl : cc, -1);
2005 maybe_rect(dr, RECT(4,4),
2006 (val & (PIECE_BRCORNER | PIECE_BBORDER |
2007 PIECE_RBORDER)) ? -1 : cc, -1);
2008
2009#undef RECT
2010}
2011
2012static void draw_tile(drawing *dr, game_drawstate *ds,
2013 int x, int y, unsigned long val)
2014{
2015 int tx = COORD(x), ty = COORD(y);
2016 int cc, ch, cl;
2017
2018 /*
2019 * Draw the tile background.
2020 */
2021 if (val & BG_TARGET)
2022 cc = COL_TARGET;
2023 else
2024 cc = COL_BACKGROUND;
2025 ch = cc+1;
2026 cl = cc+2;
2027 if (val & FLASH_LOW)
2028 cc = cl;
2029 else if (val & FLASH_HIGH)
2030 cc = ch;
2031
2032 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc);
2033 if (val & BG_FORCEFIELD) {
2034 /*
2035 * Cattle-grid effect to indicate that nothing but the
2036 * main block can slide over this square.
2037 */
2038 int n = 3 * (TILESIZE / (3*HIGHLIGHT_WIDTH));
2039 int i;
2040
2041 for (i = 1; i < n; i += 3) {
2042 draw_rect(dr, tx,ty+(TILESIZE*i/n), TILESIZE,HIGHLIGHT_WIDTH, cl);
2043 draw_rect(dr, tx+(TILESIZE*i/n),ty, HIGHLIGHT_WIDTH,TILESIZE, cl);
2044 }
2045 }
2046
2047 /*
2048 * Draw the tile midground: a shadow of a block, for
2049 * displaying partial solutions.
2050 */
2051 if (val & FG_SHADOW) {
2052 draw_piecepart(dr, ds, tx, ty, (val >> FG_SHADOWSH) & PIECE_MASK,
2053 cl, cl, cl);
2054 }
2055
2056 /*
2057 * Draw the tile foreground, i.e. some section of a block or
2058 * wall.
2059 */
2060 if (val & FG_WALL) {
2061 cc = COL_BACKGROUND;
2062 ch = cc+1;
2063 cl = cc+2;
2064 if (val & FLASH_LOW)
2065 cc = cl;
2066 else if (val & FLASH_HIGH)
2067 cc = ch;
2068
2069 draw_wallpart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK,
2070 cl, cc, ch);
2071 } else if (val & (FG_MAIN | FG_NORMAL)) {
2072 if (val & FG_DRAGGING)
2073 cc = (val & FG_MAIN ? COL_MAIN_DRAGGING : COL_DRAGGING);
2074 else
2075 cc = (val & FG_MAIN ? COL_MAIN : COL_BACKGROUND);
2076 ch = cc+1;
2077 cl = cc+2;
2078
2079 if (val & FLASH_LOW)
2080 cc = cl;
2081 else if (val & (FLASH_HIGH | FG_SOLVEPIECE))
2082 cc = ch;
2083
2084 draw_piecepart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK,
2085 cl, cc, ch);
2086 }
2087
2088 draw_update(dr, tx, ty, TILESIZE, TILESIZE);
2089}
2090
2091static unsigned long find_piecepart(int w, int h, int *dsf, int x, int y)
2092{
2093 int i = y*w+x;
2094 int canon = dsf_canonify(dsf, i);
2095 unsigned long val = 0;
2096
2097 if (x == 0 || canon != dsf_canonify(dsf, i-1))
2098 val |= PIECE_LBORDER;
2099 if (y== 0 || canon != dsf_canonify(dsf, i-w))
2100 val |= PIECE_TBORDER;
2101 if (x == w-1 || canon != dsf_canonify(dsf, i+1))
2102 val |= PIECE_RBORDER;
2103 if (y == h-1 || canon != dsf_canonify(dsf, i+w))
2104 val |= PIECE_BBORDER;
2105 if (!(val & (PIECE_TBORDER | PIECE_LBORDER)) &&
2106 canon != dsf_canonify(dsf, i-1-w))
2107 val |= PIECE_TLCORNER;
2108 if (!(val & (PIECE_TBORDER | PIECE_RBORDER)) &&
2109 canon != dsf_canonify(dsf, i+1-w))
2110 val |= PIECE_TRCORNER;
2111 if (!(val & (PIECE_BBORDER | PIECE_LBORDER)) &&
2112 canon != dsf_canonify(dsf, i-1+w))
2113 val |= PIECE_BLCORNER;
2114 if (!(val & (PIECE_BBORDER | PIECE_RBORDER)) &&
2115 canon != dsf_canonify(dsf, i+1+w))
2116 val |= PIECE_BRCORNER;
2117 return val;
2118}
2119
2120static void game_redraw(drawing *dr, game_drawstate *ds,
2121 const game_state *oldstate, const game_state *state,
2122 int dir, const game_ui *ui,
2123 float animtime, float flashtime)
2124{
2125 int w = state->w, h = state->h, wh = w*h;
2126 unsigned char *board;
2127 int *dsf;
2128 int x, y, mainanchor, mainpos, dragpos, solvepos, solvesrc, solvedst;
2129
2130 if (!ds->started) {
2131 /*
2132 * The initial contents of the window are not guaranteed
2133 * and can vary with front ends. To be on the safe side,
2134 * all games should start by drawing a big
2135 * background-colour rectangle covering the whole window.
2136 */
2137 draw_rect(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize, COL_BACKGROUND);
2138 ds->started = TRUE;
2139 }
2140
2141 /*
2142 * Construct the board we'll be displaying (which may be
2143 * different from the one in state if ui describes a drag in
2144 * progress).
2145 */
2146 board = snewn(wh, unsigned char);
2147 memcpy(board, state->board, wh);
2148 if (ui->dragging) {
2149 int mpret = move_piece(w, h, state->board, board,
2150 state->imm->forcefield,
2151 ui->drag_anchor, ui->drag_currpos);
2152 assert(mpret);
2153 }
2154
2155 if (state->soln) {
2156 solvesrc = state->soln->moves[state->soln_index*2];
2157 solvedst = state->soln->moves[state->soln_index*2+1];
2158 if (solvesrc == state->lastmoved_pos)
2159 solvesrc = state->lastmoved;
2160 if (solvesrc == ui->drag_anchor)
2161 solvesrc = ui->drag_currpos;
2162 } else
2163 solvesrc = solvedst = -1;
2164
2165 /*
2166 * Build a dsf out of that board, so we can conveniently tell
2167 * which edges are connected and which aren't.
2168 */
2169 dsf = snew_dsf(wh);
2170 mainanchor = -1;
2171 for (y = 0; y < h; y++)
2172 for (x = 0; x < w; x++) {
2173 int i = y*w+x;
2174
2175 if (ISDIST(board[i]))
2176 dsf_merge(dsf, i, i - board[i]);
2177 if (board[i] == MAINANCHOR)
2178 mainanchor = i;
2179 if (board[i] == WALL) {
2180 if (x > 0 && board[i-1] == WALL)
2181 dsf_merge(dsf, i, i-1);
2182 if (y > 0 && board[i-w] == WALL)
2183 dsf_merge(dsf, i, i-w);
2184 }
2185 }
2186 assert(mainanchor >= 0);
2187 mainpos = dsf_canonify(dsf, mainanchor);
2188 dragpos = ui->drag_currpos > 0 ? dsf_canonify(dsf, ui->drag_currpos) : -1;
2189 solvepos = solvesrc >= 0 ? dsf_canonify(dsf, solvesrc) : -1;
2190
2191 /*
2192 * Now we can construct the data about what we want to draw.
2193 */
2194 for (y = 0; y < h; y++)
2195 for (x = 0; x < w; x++) {
2196 int i = y*w+x;
2197 int j;
2198 unsigned long val;
2199 int canon;
2200
2201 /*
2202 * See if this square is part of the target area.
2203 */
2204 j = i + mainanchor - (state->ty * w + state->tx);
2205 while (j >= 0 && j < wh && ISDIST(board[j]))
2206 j -= board[j];
2207 if (j == mainanchor)
2208 val = BG_TARGET;
2209 else
2210 val = BG_NORMAL;
2211
2212 if (state->imm->forcefield[i])
2213 val |= BG_FORCEFIELD;
2214
2215 if (flashtime > 0) {
2216 int flashtype = (int)(flashtime / FLASH_INTERVAL) & 1;
2217 val |= (flashtype ? FLASH_LOW : FLASH_HIGH);
2218 }
2219
2220 if (board[i] != EMPTY) {
2221 canon = dsf_canonify(dsf, i);
2222
2223 if (board[i] == WALL)
2224 val |= FG_WALL;
2225 else if (canon == mainpos)
2226 val |= FG_MAIN;
2227 else
2228 val |= FG_NORMAL;
2229 if (canon == dragpos)
2230 val |= FG_DRAGGING;
2231 if (canon == solvepos)
2232 val |= FG_SOLVEPIECE;
2233
2234 /*
2235 * Now look around to see if other squares
2236 * belonging to the same block are adjacent to us.
2237 */
2238 val |= find_piecepart(w, h, dsf, x, y) << FG_MAINPIECESH;
2239 }
2240
2241 /*
2242 * If we're in the middle of showing a solution,
2243 * display a shadow piece for the target of the
2244 * current move.
2245 */
2246 if (solvepos >= 0) {
2247 int si = i - solvedst + solvesrc;
2248 if (si >= 0 && si < wh && dsf_canonify(dsf, si) == solvepos) {
2249 val |= find_piecepart(w, h, dsf,
2250 si % w, si / w) << FG_SHADOWSH;
2251 val |= FG_SHADOW;
2252 }
2253 }
2254
2255 if (val != ds->grid[i]) {
2256 draw_tile(dr, ds, x, y, val);
2257 ds->grid[i] = val;
2258 }
2259 }
2260
2261 /*
2262 * Update the status bar.
2263 */
2264 {
2265 char statusbuf[256];
2266
2267 sprintf(statusbuf, "%sMoves: %d",
2268 (state->completed >= 0 ?
2269 (state->cheated ? "Auto-solved. " : "COMPLETED! ") :
2270 (state->cheated ? "Auto-solver used. " : "")),
2271 (state->completed >= 0 ? state->completed : state->movecount));
2272 if (state->minmoves >= 0)
2273 sprintf(statusbuf+strlen(statusbuf), " (min %d)",
2274 state->minmoves);
2275
2276 status_bar(dr, statusbuf);
2277 }
2278
2279 sfree(dsf);
2280 sfree(board);
2281}
2282
2283static float game_anim_length(const game_state *oldstate,
2284 const game_state *newstate, int dir, game_ui *ui)
2285{
2286 return 0.0F;
2287}
2288
2289static float game_flash_length(const game_state *oldstate,
2290 const game_state *newstate, int dir, game_ui *ui)
2291{
2292 if (oldstate->completed < 0 && newstate->completed >= 0)
2293 return FLASH_TIME;
2294
2295 return 0.0F;
2296}
2297
2298static int game_status(const game_state *state)
2299{
2300 return state->completed ? +1 : 0;
2301}
2302
2303static int game_timing_state(const game_state *state, game_ui *ui)
2304{
2305 return TRUE;
2306}
2307
2308static void game_print_size(const game_params *params, float *x, float *y)
2309{
2310}
2311
2312static void game_print(drawing *dr, const game_state *state, int tilesize)
2313{
2314}
2315
2316#ifdef COMBINED
2317#define thegame slide
2318#endif
2319
2320const struct game thegame = {
2321 "Slide", NULL, NULL,
2322 default_params,
2323 game_fetch_preset, NULL,
2324 decode_params,
2325 encode_params,
2326 free_params,
2327 dup_params,
2328 TRUE, game_configure, custom_params,
2329 validate_params,
2330 new_game_desc,
2331 validate_desc,
2332 new_game,
2333 dup_game,
2334 free_game,
2335 TRUE, solve_game,
2336 TRUE, game_can_format_as_text_now, game_text_format,
2337 new_ui,
2338 free_ui,
2339 encode_ui,
2340 decode_ui,
2341 game_changed_state,
2342 interpret_move,
2343 execute_move,
2344 PREFERRED_TILESIZE, game_compute_size, game_set_size,
2345 game_colours,
2346 game_new_drawstate,
2347 game_free_drawstate,
2348 game_redraw,
2349 game_anim_length,
2350 game_flash_length,
2351 game_status,
2352 FALSE, FALSE, game_print_size, game_print,
2353 TRUE, /* wants_statusbar */
2354 FALSE, game_timing_state,
2355 0, /* flags */
2356};
2357
2358#ifdef STANDALONE_SOLVER
2359
2360#include <stdarg.h>
2361
2362int main(int argc, char **argv)
2363{
2364 game_params *p;
2365 game_state *s;
2366 char *id = NULL, *desc, *err;
2367 int count = FALSE;
2368 int ret;
2369 int *moves;
2370
2371 while (--argc > 0) {
2372 char *p = *++argv;
2373 /*
2374 if (!strcmp(p, "-v")) {
2375 verbose = TRUE;
2376 } else
2377 */
2378 if (!strcmp(p, "-c")) {
2379 count = TRUE;
2380 } else if (*p == '-') {
2381 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2382 return 1;
2383 } else {
2384 id = p;
2385 }
2386 }
2387
2388 if (!id) {
2389 fprintf(stderr, "usage: %s [-c | -v] <game_id>\n", argv[0]);
2390 return 1;
2391 }
2392
2393 desc = strchr(id, ':');
2394 if (!desc) {
2395 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2396 return 1;
2397 }
2398 *desc++ = '\0';
2399
2400 p = default_params();
2401 decode_params(p, id);
2402 err = validate_desc(p, desc);
2403 if (err) {
2404 fprintf(stderr, "%s: %s\n", argv[0], err);
2405 return 1;
2406 }
2407 s = new_game(NULL, p, desc);
2408
2409 ret = solve_board(s->w, s->h, s->board, s->imm->forcefield,
2410 s->tx, s->ty, -1, &moves);
2411 if (ret < 0) {
2412 printf("No solution found\n");
2413 } else {
2414 int index = 0;
2415 if (count) {
2416 printf("%d moves required\n", ret);
2417 return 0;
2418 }
2419 while (1) {
2420 int moveret;
2421 char *text = board_text_format(s->w, s->h, s->board,
2422 s->imm->forcefield);
2423 game_state *s2;
2424
2425 printf("position %d:\n%s", index, text);
2426
2427 if (index >= ret)
2428 break;
2429
2430 s2 = dup_game(s);
2431 moveret = move_piece(s->w, s->h, s->board,
2432 s2->board, s->imm->forcefield,
2433 moves[index*2], moves[index*2+1]);
2434 assert(moveret);
2435
2436 free_game(s);
2437 s = s2;
2438 index++;
2439 }
2440 }
2441
2442 return 0;
2443}
2444
2445#endif
diff --git a/apps/plugins/puzzles/src/unfinished/sokoban.R b/apps/plugins/puzzles/src/unfinished/sokoban.R
new file mode 100644
index 0000000000..3b6dab56ba
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/sokoban.R
@@ -0,0 +1,19 @@
1# -*- makefile -*-
2
3sokoban : [X] GTK COMMON sokoban sokoban-icon|no-icon
4
5sokoban : [G] WINDOWS COMMON sokoban sokoban.res?
6
7ALL += sokoban[COMBINED]
8
9!begin am gtk
10GAMES += sokoban
11!end
12
13!begin >list.c
14 A(sokoban) \
15!end
16
17!begin >gamedesc.txt
18sokoban:sokoban.exe:Sokoban:Barrel-pushing puzzle:Push all the barrels into the target squares.
19!end
diff --git a/apps/plugins/puzzles/src/unfinished/sokoban.c b/apps/plugins/puzzles/src/unfinished/sokoban.c
new file mode 100644
index 0000000000..2f0af35bc2
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/sokoban.c
@@ -0,0 +1,1479 @@
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#include <math.h>
62
63#include "puzzles.h"
64
65/*
66 * Various subsets of these constants are used during game
67 * generation, game play, game IDs and the game_drawstate.
68 */
69#define INITIAL 'i' /* used only in game generation */
70#define SPACE 's'
71#define WALL 'w'
72#define PIT 'p'
73#define DEEP_PIT 'd'
74#define TARGET 't'
75#define BARREL 'b'
76#define BARRELTARGET 'f' /* target is 'f'illed */
77#define PLAYER 'u' /* yo'u'; used in game IDs */
78#define PLAYERTARGET 'v' /* bad letter: v is to u as t is to s */
79#define INVALID '!' /* used in drawstate to force redraw */
80/*
81 * We also support the use of any capital letter as a barrel, which
82 * will be displayed with that letter as a label. (This facilitates
83 * people distributing annotated game IDs for particular Sokoban
84 * levels, so they can accompany them with verbal instructions
85 * about pushing particular barrels in particular ways.) Therefore,
86 * to find out whether something is a barrel, we need a test
87 * function which does a bit more than just comparing to BARREL.
88 *
89 * When resting on target squares, capital-letter barrels are
90 * replaced with their control-character value (A -> ^A).
91 */
92#define IS_PLAYER(c) ( (c)==PLAYER || (c)==PLAYERTARGET )
93#define IS_BARREL(c) ( (c)==BARREL || (c)==BARRELTARGET || \
94 ((c)>='A' && (c)<='Z') || ((c)>=1 && (c)<=26) )
95#define IS_ON_TARGET(c) ( (c)==TARGET || (c)==BARRELTARGET || \
96 (c)==PLAYERTARGET || ((c)>=1 && (c)<=26) )
97#define TARGETISE(b) ( (b)==BARREL ? BARRELTARGET : (b)-('A'-1) )
98#define DETARGETISE(b) ( (b)==BARRELTARGET ? BARREL : (b)+('A'-1) )
99#define BARREL_LABEL(b) ( (b)>='A'&&(b)<='Z' ? (b) : \
100 (b)>=1 && (b)<=26 ? (b)+('A'-1) : 0 )
101
102#define DX(d) (d == 0 ? -1 : d == 2 ? +1 : 0)
103#define DY(d) (d == 1 ? -1 : d == 3 ? +1 : 0)
104
105#define FLASH_LENGTH 0.3F
106
107enum {
108 COL_BACKGROUND,
109 COL_TARGET,
110 COL_PIT,
111 COL_DEEP_PIT,
112 COL_BARREL,
113 COL_PLAYER,
114 COL_TEXT,
115 COL_GRID,
116 COL_OUTLINE,
117 COL_HIGHLIGHT,
118 COL_LOWLIGHT,
119 COL_WALL,
120 NCOLOURS
121};
122
123struct game_params {
124 int w, h;
125 /*
126 * FIXME: a parameter involving degree of filling in?
127 */
128};
129
130struct game_state {
131 game_params p;
132 unsigned char *grid;
133 int px, py;
134 int completed;
135};
136
137static game_params *default_params(void)
138{
139 game_params *ret = snew(game_params);
140
141 ret->w = 12;
142 ret->h = 10;
143
144 return ret;
145}
146
147static void free_params(game_params *params)
148{
149 sfree(params);
150}
151
152static game_params *dup_params(const game_params *params)
153{
154 game_params *ret = snew(game_params);
155 *ret = *params; /* structure copy */
156 return ret;
157}
158
159static const struct game_params sokoban_presets[] = {
160 { 12, 10 },
161 { 16, 12 },
162 { 20, 16 },
163};
164
165static int game_fetch_preset(int i, char **name, game_params **params)
166{
167 game_params p, *ret;
168 char *retname;
169 char namebuf[80];
170
171 if (i < 0 || i >= lenof(sokoban_presets))
172 return FALSE;
173
174 p = sokoban_presets[i];
175 ret = dup_params(&p);
176 sprintf(namebuf, "%dx%d", ret->w, ret->h);
177 retname = dupstr(namebuf);
178
179 *params = ret;
180 *name = retname;
181 return TRUE;
182}
183
184static void decode_params(game_params *params, char const *string)
185{
186 params->w = params->h = atoi(string);
187 while (*string && isdigit((unsigned char)*string)) string++;
188 if (*string == 'x') {
189 string++;
190 params->h = atoi(string);
191 }
192}
193
194static char *encode_params(const game_params *params, int full)
195{
196 char data[256];
197
198 sprintf(data, "%dx%d", params->w, params->h);
199
200 return dupstr(data);
201}
202
203static config_item *game_configure(const game_params *params)
204{
205 config_item *ret;
206 char buf[80];
207
208 ret = snewn(3, config_item);
209
210 ret[0].name = "Width";
211 ret[0].type = C_STRING;
212 sprintf(buf, "%d", params->w);
213 ret[0].sval = dupstr(buf);
214 ret[0].ival = 0;
215
216 ret[1].name = "Height";
217 ret[1].type = C_STRING;
218 sprintf(buf, "%d", params->h);
219 ret[1].sval = dupstr(buf);
220 ret[1].ival = 0;
221
222 ret[2].name = NULL;
223 ret[2].type = C_END;
224 ret[2].sval = NULL;
225 ret[2].ival = 0;
226
227 return ret;
228}
229
230static game_params *custom_params(const config_item *cfg)
231{
232 game_params *ret = snew(game_params);
233
234 ret->w = atoi(cfg[0].sval);
235 ret->h = atoi(cfg[1].sval);
236
237 return ret;
238}
239
240static char *validate_params(const game_params *params, int 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
306static void sokoban_generate(int w, int h, unsigned char *grid, int moves,
307 int 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
734static char *new_game_desc(const game_params *params, random_state *rs,
735 char **aux, int 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
809static 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
846static 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
884static 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
899static void free_game(game_state *state)
900{
901 sfree(state->grid);
902 sfree(state);
903}
904
905static char *solve_game(const game_state *state, const game_state *currstate,
906 const char *aux, char **error)
907{
908 return NULL;
909}
910
911static int game_can_format_as_text_now(const game_params *params)
912{
913 return TRUE;
914}
915
916static char *game_text_format(const game_state *state)
917{
918 return NULL;
919}
920
921static game_ui *new_ui(const game_state *state)
922{
923 return NULL;
924}
925
926static void free_ui(game_ui *ui)
927{
928}
929
930static char *encode_ui(const game_ui *ui)
931{
932 return NULL;
933}
934
935static void decode_ui(game_ui *ui, const char *encoding)
936{
937}
938
939static void game_changed_state(game_ui *ui, const game_state *oldstate,
940 const game_state *newstate)
941{
942}
943
944struct game_drawstate {
945 game_params p;
946 int tilesize;
947 int started;
948 unsigned short *grid;
949};
950
951#define PREFERRED_TILESIZE 32
952#define TILESIZE (ds->tilesize)
953#define BORDER (TILESIZE)
954#define HIGHLIGHT_WIDTH (TILESIZE / 10)
955#define COORD(x) ( (x) * TILESIZE + BORDER )
956#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
957
958/*
959 * I'm going to need to do most of the move-type analysis in both
960 * interpret_move and execute_move, so I'll abstract it out into a
961 * subfunction. move_type() returns -1 for an illegal move, 0 for a
962 * movement, and 1 for a push.
963 */
964int move_type(game_state *state, int dx, int dy)
965{
966 int w = state->p.w, h = state->p.h;
967 int px = state->px, py = state->py;
968 int nx, ny, nbx, nby;
969
970 assert(dx >= -1 && dx <= +1);
971 assert(dy >= -1 && dy <= +1);
972 assert(dx || dy);
973
974 nx = px + dx;
975 ny = py + dy;
976
977 /*
978 * Disallow any move that goes off the grid.
979 */
980 if (nx < 0 || nx >= w || ny < 0 || ny >= h)
981 return -1;
982
983 /*
984 * Examine the target square of the move to see whether it's a
985 * space, a barrel, or a wall.
986 */
987
988 if (state->grid[ny*w+nx] == WALL ||
989 state->grid[ny*w+nx] == PIT ||
990 state->grid[ny*w+nx] == DEEP_PIT)
991 return -1; /* this one's easy; just disallow it */
992
993 if (IS_BARREL(state->grid[ny*w+nx])) {
994 /*
995 * This is a push move. For a start, that means it must not
996 * be diagonal.
997 */
998 if (dy && dx)
999 return -1;
1000
1001 /*
1002 * Now find the location of the third square involved in
1003 * the push, and stop if it's off the edge.
1004 */
1005 nbx = nx + dx;
1006 nby = ny + dy;
1007 if (nbx < 0 || nbx >= w || nby < 0 || nby >= h)
1008 return -1;
1009
1010 /*
1011 * That third square must be able to accept a barrel.
1012 */
1013 if (state->grid[nby*w+nbx] == SPACE ||
1014 state->grid[nby*w+nbx] == TARGET ||
1015 state->grid[nby*w+nbx] == PIT ||
1016 state->grid[nby*w+nbx] == DEEP_PIT) {
1017 /*
1018 * The push is valid.
1019 */
1020 return 1;
1021 } else {
1022 return -1;
1023 }
1024 } else {
1025 /*
1026 * This is just an ordinary move. We've already checked the
1027 * target square, so the only thing left to check is that a
1028 * diagonal move has a space on one side to have notionally
1029 * gone through.
1030 */
1031 if (dx && dy &&
1032 state->grid[(py+dy)*w+px] != SPACE &&
1033 state->grid[(py+dy)*w+px] != TARGET &&
1034 state->grid[py*w+(px+dx)] != SPACE &&
1035 state->grid[py*w+(px+dx)] != TARGET)
1036 return -1;
1037
1038 /*
1039 * Otherwise, the move is valid.
1040 */
1041 return 0;
1042 }
1043}
1044
1045static char *interpret_move(const game_state *state, game_ui *ui,
1046 const game_drawstate *ds,
1047 int x, int y, int button)
1048{
1049 int dx=0, dy=0;
1050 char *move;
1051
1052 /*
1053 * Diagonal movement is supported as it is in NetHack: it's
1054 * for movement only (never pushing), and one of the two
1055 * squares adjacent to both the source and destination
1056 * squares must be free to move through. In other words, it
1057 * is only a shorthand for two orthogonal moves and cannot
1058 * change the nature of the actual puzzle game.
1059 */
1060 if (button == CURSOR_UP || button == (MOD_NUM_KEYPAD | '8'))
1061 dx = 0, dy = -1;
1062 else if (button == CURSOR_DOWN || button == (MOD_NUM_KEYPAD | '2'))
1063 dx = 0, dy = +1;
1064 else if (button == CURSOR_LEFT || button == (MOD_NUM_KEYPAD | '4'))
1065 dx = -1, dy = 0;
1066 else if (button == CURSOR_RIGHT || button == (MOD_NUM_KEYPAD | '6'))
1067 dx = +1, dy = 0;
1068 else if (button == (MOD_NUM_KEYPAD | '7'))
1069 dx = -1, dy = -1;
1070 else if (button == (MOD_NUM_KEYPAD | '9'))
1071 dx = +1, dy = -1;
1072 else if (button == (MOD_NUM_KEYPAD | '1'))
1073 dx = -1, dy = +1;
1074 else if (button == (MOD_NUM_KEYPAD | '3'))
1075 dx = +1, dy = +1;
1076 else if (button == LEFT_BUTTON)
1077 {
1078 if(x < COORD(state->px))
1079 dx = -1;
1080 else if (x > COORD(state->px + 1))
1081 dx = 1;
1082 if(y < COORD(state->py))
1083 dy = -1;
1084 else if (y > COORD(state->py + 1))
1085 dy = 1;
1086 }
1087 else
1088 return NULL;
1089
1090 if((dx == 0) && (dy == 0))
1091 return(NULL);
1092
1093 if (move_type(state, dx, dy) < 0)
1094 return NULL;
1095
1096 move = snewn(2, char);
1097 move[1] = '\0';
1098 move[0] = '5' - 3*dy + dx;
1099 return move;
1100}
1101
1102static game_state *execute_move(const game_state *state, const char *move)
1103{
1104 int w = state->p.w, h = state->p.h;
1105 int px = state->px, py = state->py;
1106 int dx, dy, nx, ny, nbx, nby, type, m, i, freebarrels, freetargets;
1107 game_state *ret;
1108
1109 if (*move < '1' || *move == '5' || *move > '9' || move[1])
1110 return NULL; /* invalid move string */
1111
1112 m = *move - '0';
1113 dx = (m + 2) % 3 - 1;
1114 dy = 2 - (m + 2) / 3;
1115 type = move_type(state, dx, dy);
1116 if (type < 0)
1117 return NULL;
1118
1119 ret = dup_game(state);
1120
1121 nx = px + dx;
1122 ny = py + dy;
1123 nbx = nx + dx;
1124 nby = ny + dy;
1125
1126 if (type) {
1127 int b;
1128
1129 /*
1130 * Push.
1131 */
1132 b = ret->grid[ny*w+nx];
1133 if (IS_ON_TARGET(b)) {
1134 ret->grid[ny*w+nx] = TARGET;
1135 b = DETARGETISE(b);
1136 } else
1137 ret->grid[ny*w+nx] = SPACE;
1138
1139 if (ret->grid[nby*w+nbx] == PIT)
1140 ret->grid[nby*w+nbx] = SPACE;
1141 else if (ret->grid[nby*w+nbx] == DEEP_PIT)
1142 /* do nothing - the pit eats the barrel and remains there */;
1143 else if (ret->grid[nby*w+nbx] == TARGET)
1144 ret->grid[nby*w+nbx] = TARGETISE(b);
1145 else
1146 ret->grid[nby*w+nbx] = b;
1147 }
1148
1149 ret->px = nx;
1150 ret->py = ny;
1151
1152 /*
1153 * Check for completion. This is surprisingly complicated,
1154 * given the presence of pits and deep pits, and also the fact
1155 * that some Sokoban levels with pits have fewer pits than
1156 * barrels (due to providing spares, e.g. NetHack's). I think
1157 * the completion condition in fact must be that the game
1158 * cannot become any _more_ complete. That is, _either_ there
1159 * are no remaining barrels not on targets, _or_ there is a
1160 * good reason why any such barrels cannot be placed. The only
1161 * available good reason is that there are no remaining pits,
1162 * no free target squares, and no deep pits at all.
1163 */
1164 if (!ret->completed) {
1165 freebarrels = FALSE;
1166 freetargets = FALSE;
1167 for (i = 0; i < w*h; i++) {
1168 int v = ret->grid[i];
1169
1170 if (IS_BARREL(v) && !IS_ON_TARGET(v))
1171 freebarrels = TRUE;
1172 if (v == DEEP_PIT || v == PIT ||
1173 (!IS_BARREL(v) && IS_ON_TARGET(v)))
1174 freetargets = TRUE;
1175 }
1176
1177 if (!freebarrels || !freetargets)
1178 ret->completed = TRUE;
1179 }
1180
1181 return ret;
1182}
1183
1184/* ----------------------------------------------------------------------
1185 * Drawing routines.
1186 */
1187
1188static void game_compute_size(const game_params *params, int tilesize,
1189 int *x, int *y)
1190{
1191 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1192 struct { int tilesize; } ads, *ds = &ads;
1193 ads.tilesize = tilesize;
1194
1195 *x = 2 * BORDER + 1 + params->w * TILESIZE;
1196 *y = 2 * BORDER + 1 + params->h * TILESIZE;
1197}
1198
1199static void game_set_size(drawing *dr, game_drawstate *ds,
1200 const game_params *params, int tilesize)
1201{
1202 ds->tilesize = tilesize;
1203}
1204
1205static float *game_colours(frontend *fe, int *ncolours)
1206{
1207 float *ret = snewn(3 * NCOLOURS, float);
1208 int i;
1209
1210 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1211
1212 ret[COL_OUTLINE * 3 + 0] = 0.0F;
1213 ret[COL_OUTLINE * 3 + 1] = 0.0F;
1214 ret[COL_OUTLINE * 3 + 2] = 0.0F;
1215
1216 ret[COL_PLAYER * 3 + 0] = 0.0F;
1217 ret[COL_PLAYER * 3 + 1] = 1.0F;
1218 ret[COL_PLAYER * 3 + 2] = 0.0F;
1219
1220 ret[COL_BARREL * 3 + 0] = 0.6F;
1221 ret[COL_BARREL * 3 + 1] = 0.3F;
1222 ret[COL_BARREL * 3 + 2] = 0.0F;
1223
1224 ret[COL_TARGET * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0];
1225 ret[COL_TARGET * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1];
1226 ret[COL_TARGET * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2];
1227
1228 ret[COL_PIT * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0] / 2;
1229 ret[COL_PIT * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1] / 2;
1230 ret[COL_PIT * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2] / 2;
1231
1232 ret[COL_DEEP_PIT * 3 + 0] = 0.0F;
1233 ret[COL_DEEP_PIT * 3 + 1] = 0.0F;
1234 ret[COL_DEEP_PIT * 3 + 2] = 0.0F;
1235
1236 ret[COL_TEXT * 3 + 0] = 1.0F;
1237 ret[COL_TEXT * 3 + 1] = 1.0F;
1238 ret[COL_TEXT * 3 + 2] = 1.0F;
1239
1240 ret[COL_GRID * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0];
1241 ret[COL_GRID * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1];
1242 ret[COL_GRID * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2];
1243
1244 ret[COL_OUTLINE * 3 + 0] = 0.0F;
1245 ret[COL_OUTLINE * 3 + 1] = 0.0F;
1246 ret[COL_OUTLINE * 3 + 2] = 0.0F;
1247
1248 for (i = 0; i < 3; i++) {
1249 ret[COL_WALL * 3 + i] = (3 * ret[COL_BACKGROUND * 3 + i] +
1250 1 * ret[COL_HIGHLIGHT * 3 + i]) / 4;
1251 }
1252
1253 *ncolours = NCOLOURS;
1254 return ret;
1255}
1256
1257static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1258{
1259 int w = state->p.w, h = state->p.h;
1260 struct game_drawstate *ds = snew(struct game_drawstate);
1261 int i;
1262
1263 ds->tilesize = 0;
1264 ds->p = state->p; /* structure copy */
1265 ds->grid = snewn(w*h, unsigned short);
1266 for (i = 0; i < w*h; i++)
1267 ds->grid[i] = INVALID;
1268 ds->started = FALSE;
1269
1270 return ds;
1271}
1272
1273static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1274{
1275 sfree(ds->grid);
1276 sfree(ds);
1277}
1278
1279static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, int v)
1280{
1281 int tx = COORD(x), ty = COORD(y);
1282 int bg = (v & 0x100 ? COL_HIGHLIGHT : COL_BACKGROUND);
1283
1284 v &= 0xFF;
1285
1286 clip(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1);
1287 draw_rect(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1, bg);
1288
1289 if (v == WALL) {
1290 int coords[6];
1291
1292 coords[0] = tx + TILESIZE;
1293 coords[1] = ty + TILESIZE;
1294 coords[2] = tx + TILESIZE;
1295 coords[3] = ty + 1;
1296 coords[4] = tx + 1;
1297 coords[5] = ty + TILESIZE;
1298 draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT);
1299
1300 coords[0] = tx + 1;
1301 coords[1] = ty + 1;
1302 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
1303
1304 draw_rect(dr, tx + 1 + HIGHLIGHT_WIDTH, ty + 1 + HIGHLIGHT_WIDTH,
1305 TILESIZE - 2*HIGHLIGHT_WIDTH,
1306 TILESIZE - 2*HIGHLIGHT_WIDTH, COL_WALL);
1307 } else if (v == PIT) {
1308 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1309 TILESIZE*3/7, COL_PIT, COL_OUTLINE);
1310 } else if (v == DEEP_PIT) {
1311 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1312 TILESIZE*3/7, COL_DEEP_PIT, COL_OUTLINE);
1313 } else {
1314 if (IS_ON_TARGET(v)) {
1315 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1316 TILESIZE*3/7, COL_TARGET, COL_OUTLINE);
1317 }
1318 if (IS_PLAYER(v)) {
1319 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1320 TILESIZE/3, COL_PLAYER, COL_OUTLINE);
1321 } else if (IS_BARREL(v)) {
1322 char str[2];
1323
1324 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1325 TILESIZE/3, COL_BARREL, COL_OUTLINE);
1326 str[1] = '\0';
1327 str[0] = BARREL_LABEL(v);
1328 if (str[0]) {
1329 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1330 FONT_VARIABLE, TILESIZE/2,
1331 ALIGN_VCENTRE | ALIGN_HCENTRE, COL_TEXT, str);
1332 }
1333 }
1334 }
1335
1336 unclip(dr);
1337 draw_update(dr, tx, ty, TILESIZE, TILESIZE);
1338}
1339
1340static void game_redraw(drawing *dr, game_drawstate *ds,
1341 const game_state *oldstate, const game_state *state,
1342 int dir, const game_ui *ui,
1343 float animtime, float flashtime)
1344{
1345 int w = state->p.w, h = state->p.h /*, wh = w*h */;
1346 int x, y;
1347 int flashtype;
1348
1349 if (flashtime &&
1350 !((int)(flashtime * 3 / FLASH_LENGTH) % 2))
1351 flashtype = 0x100;
1352 else
1353 flashtype = 0;
1354
1355 /*
1356 * Initialise a fresh drawstate.
1357 */
1358 if (!ds->started) {
1359 int wid, ht;
1360
1361 /*
1362 * Blank out the window initially.
1363 */
1364 game_compute_size(&ds->p, TILESIZE, &wid, &ht);
1365 draw_rect(dr, 0, 0, wid, ht, COL_BACKGROUND);
1366 draw_update(dr, 0, 0, wid, ht);
1367
1368 /*
1369 * Draw the grid lines.
1370 */
1371 for (y = 0; y <= h; y++)
1372 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y),
1373 COL_LOWLIGHT);
1374 for (x = 0; x <= w; x++)
1375 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h),
1376 COL_LOWLIGHT);
1377
1378 ds->started = TRUE;
1379 }
1380
1381 /*
1382 * Draw the grid contents.
1383 */
1384 for (y = 0; y < h; y++)
1385 for (x = 0; x < w; x++) {
1386 int v = state->grid[y*w+x];
1387 if (y == state->py && x == state->px) {
1388 if (v == TARGET)
1389 v = PLAYERTARGET;
1390 else {
1391 assert(v == SPACE);
1392 v = PLAYER;
1393 }
1394 }
1395
1396 v |= flashtype;
1397
1398 if (ds->grid[y*w+x] != v) {
1399 draw_tile(dr, ds, x, y, v);
1400 ds->grid[y*w+x] = v;
1401 }
1402 }
1403
1404}
1405
1406static float game_anim_length(const game_state *oldstate,
1407 const game_state *newstate, int dir, game_ui *ui)
1408{
1409 return 0.0F;
1410}
1411
1412static float game_flash_length(const game_state *oldstate,
1413 const game_state *newstate, int dir, game_ui *ui)
1414{
1415 if (!oldstate->completed && newstate->completed)
1416 return FLASH_LENGTH;
1417 else
1418 return 0.0F;
1419}
1420
1421static int game_status(const game_state *state)
1422{
1423 return state->completed ? +1 : 0;
1424}
1425
1426static int game_timing_state(const game_state *state, game_ui *ui)
1427{
1428 return TRUE;
1429}
1430
1431static void game_print_size(const game_params *params, float *x, float *y)
1432{
1433}
1434
1435static void game_print(drawing *dr, const game_state *state, int tilesize)
1436{
1437}
1438
1439#ifdef COMBINED
1440#define thegame sokoban
1441#endif
1442
1443const struct game thegame = {
1444 "Sokoban", NULL, NULL,
1445 default_params,
1446 game_fetch_preset, NULL,
1447 decode_params,
1448 encode_params,
1449 free_params,
1450 dup_params,
1451 TRUE, game_configure, custom_params,
1452 validate_params,
1453 new_game_desc,
1454 validate_desc,
1455 new_game,
1456 dup_game,
1457 free_game,
1458 FALSE, solve_game,
1459 FALSE, game_can_format_as_text_now, game_text_format,
1460 new_ui,
1461 free_ui,
1462 encode_ui,
1463 decode_ui,
1464 game_changed_state,
1465 interpret_move,
1466 execute_move,
1467 PREFERRED_TILESIZE, game_compute_size, game_set_size,
1468 game_colours,
1469 game_new_drawstate,
1470 game_free_drawstate,
1471 game_redraw,
1472 game_anim_length,
1473 game_flash_length,
1474 game_status,
1475 FALSE, FALSE, game_print_size, game_print,
1476 FALSE, /* wants_statusbar */
1477 FALSE, game_timing_state,
1478 0, /* flags */
1479};