summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranklin Wei <franklin@rockbox.org>2024-08-18 21:14:07 -0400
committerFranklin Wei <franklin@rockbox.org>2024-08-25 19:30:01 -0400
commiteca00638aeab59cf03287b9f298c86a6de1b5a9a (patch)
tree32f14d7c1a05f86b2ba7e91be647233570203599
parent3dd69ce23e3089ac78c512f02e59406d05302fa4 (diff)
downloadrockbox-eca00638aeab59cf03287b9f298c86a6de1b5a9a.tar.gz
rockbox-eca00638aeab59cf03287b9f298c86a6de1b5a9a.zip
puzzles: add Slide and Sokoban.
This enables two of the "unfinished" puzzles. Slide requires a new "sticky mouse mode" to enable dragging. The help system is disabled for these puzzles, since they lack manual chapters. Group is currently unplayable due to lack of request_keys() support, which will need to be added upstream. Separate fails to draw anything. Change-Id: I7bcff3679ac5b10b0f39c5eaa19a36b4b1fe8d53
-rw-r--r--apps/plugins/puzzles/SOURCES1
-rw-r--r--apps/plugins/puzzles/SOURCES.games9
-rw-r--r--apps/plugins/puzzles/SOURCES.rockbox1
-rw-r--r--apps/plugins/puzzles/compress.c1
-rw-r--r--apps/plugins/puzzles/dummy/nullhelp.c8
-rw-r--r--apps/plugins/puzzles/help.h4
-rw-r--r--apps/plugins/puzzles/help/blackbox.c1
-rw-r--r--apps/plugins/puzzles/help/bridges.c1
-rw-r--r--apps/plugins/puzzles/help/cube.c1
-rw-r--r--apps/plugins/puzzles/help/dominosa.c1
-rw-r--r--apps/plugins/puzzles/help/fifteen.c1
-rw-r--r--apps/plugins/puzzles/help/filling.c1
-rw-r--r--apps/plugins/puzzles/help/flip.c1
-rw-r--r--apps/plugins/puzzles/help/flood.c1
-rw-r--r--apps/plugins/puzzles/help/galaxies.c1
-rw-r--r--apps/plugins/puzzles/help/guess.c1
-rw-r--r--apps/plugins/puzzles/help/inertia.c1
-rw-r--r--apps/plugins/puzzles/help/keen.c1
-rw-r--r--apps/plugins/puzzles/help/lightup.c1
-rw-r--r--apps/plugins/puzzles/help/loopy.c1
-rw-r--r--apps/plugins/puzzles/help/magnets.c1
-rw-r--r--apps/plugins/puzzles/help/map.c1
-rw-r--r--apps/plugins/puzzles/help/mines.c1
-rw-r--r--apps/plugins/puzzles/help/mosaic.c1
-rw-r--r--apps/plugins/puzzles/help/net.c1
-rw-r--r--apps/plugins/puzzles/help/netslide.c1
-rw-r--r--apps/plugins/puzzles/help/palisade.c1
-rw-r--r--apps/plugins/puzzles/help/pattern.c1
-rw-r--r--apps/plugins/puzzles/help/pearl.c1
-rw-r--r--apps/plugins/puzzles/help/pegs.c1
-rw-r--r--apps/plugins/puzzles/help/range.c1
-rw-r--r--apps/plugins/puzzles/help/rect.c1
-rw-r--r--apps/plugins/puzzles/help/samegame.c1
-rw-r--r--apps/plugins/puzzles/help/signpost.c1
-rw-r--r--apps/plugins/puzzles/help/singles.c1
-rw-r--r--apps/plugins/puzzles/help/sixteen.c1
-rw-r--r--apps/plugins/puzzles/help/slant.c1
-rw-r--r--apps/plugins/puzzles/help/solo.c1
-rw-r--r--apps/plugins/puzzles/help/tents.c1
-rw-r--r--apps/plugins/puzzles/help/towers.c1
-rw-r--r--apps/plugins/puzzles/help/tracks.c1
-rw-r--r--apps/plugins/puzzles/help/twiddle.c1
-rw-r--r--apps/plugins/puzzles/help/undead.c1
-rw-r--r--apps/plugins/puzzles/help/unequal.c1
-rw-r--r--apps/plugins/puzzles/help/unruly.c1
-rw-r--r--apps/plugins/puzzles/help/untangle.c1
-rw-r--r--apps/plugins/puzzles/puzzles.make9
-rwxr-xr-xapps/plugins/puzzles/resync.sh15
-rw-r--r--apps/plugins/puzzles/rockbox.c56
-rw-r--r--apps/plugins/puzzles/src/unfinished/CMakeLists.txt31
-rw-r--r--apps/plugins/puzzles/src/unfinished/README14
-rw-r--r--apps/plugins/puzzles/src/unfinished/group.c2497
-rw-r--r--apps/plugins/puzzles/src/unfinished/group.gap97
-rw-r--r--apps/plugins/puzzles/src/unfinished/numgame.c1294
-rw-r--r--apps/plugins/puzzles/src/unfinished/path.c866
-rw-r--r--apps/plugins/puzzles/src/unfinished/separate.c861
-rw-r--r--apps/plugins/puzzles/src/unfinished/slide.c2444
-rw-r--r--apps/plugins/puzzles/src/unfinished/sokoban.c1476
58 files changed, 9698 insertions, 26 deletions
diff --git a/apps/plugins/puzzles/SOURCES b/apps/plugins/puzzles/SOURCES
index f0d2ed27f7..dfbd184ba2 100644
--- a/apps/plugins/puzzles/SOURCES
+++ b/apps/plugins/puzzles/SOURCES
@@ -3,6 +3,7 @@ rockbox.c
3rbwrappers.c 3rbwrappers.c
4rbmalloc.c 4rbmalloc.c
5lz4tiny.c 5lz4tiny.c
6dummy/nullhelp.c
6 7
7/* puzzles core sources */ 8/* puzzles core sources */
8src/combi.c 9src/combi.c
diff --git a/apps/plugins/puzzles/SOURCES.games b/apps/plugins/puzzles/SOURCES.games
index 190412295b..70f34f3334 100644
--- a/apps/plugins/puzzles/SOURCES.games
+++ b/apps/plugins/puzzles/SOURCES.games
@@ -35,13 +35,8 @@ src/undead.c
35src/unequal.c 35src/unequal.c
36src/unruly.c 36src/unruly.c
37src/untangle.c 37src/untangle.c
38 38src/unfinished/slide.c
39/* Disabled for now. Fix puzzles.make and CATEGORIES to accomodate these. */ 39src/unfinished/sokoban.c
40/* The help system would also need to be patched to compile these. */
41/*src/unfinished/group.c*/
42/*src/unfinished/separate.c*/
43/*src/unfinished/slide.c*/
44/*src/unfinished/sokoban.c*/
45 40
46/* no c200v2 */ 41/* no c200v2 */
47#if PLUGIN_BUFFER_SIZE > 0x14000 42#if PLUGIN_BUFFER_SIZE > 0x14000
diff --git a/apps/plugins/puzzles/SOURCES.rockbox b/apps/plugins/puzzles/SOURCES.rockbox
index c5bbb9af70..61ce4275ff 100644
--- a/apps/plugins/puzzles/SOURCES.rockbox
+++ b/apps/plugins/puzzles/SOURCES.rockbox
@@ -2,3 +2,4 @@ rockbox.c
2rbwrappers.c 2rbwrappers.c
3rbmalloc.c 3rbmalloc.c
4lz4tiny.c 4lz4tiny.c
5dummy/nullhelp.c
diff --git a/apps/plugins/puzzles/compress.c b/apps/plugins/puzzles/compress.c
index 127a02bf0d..d127a9af17 100644
--- a/apps/plugins/puzzles/compress.c
+++ b/apps/plugins/puzzles/compress.c
@@ -158,6 +158,7 @@ int main()
158 printf("};\n\n"); 158 printf("};\n\n");
159 printf("const unsigned short help_text_len = %d;\n", help_text_len); 159 printf("const unsigned short help_text_len = %d;\n", help_text_len);
160 printf("const unsigned short help_text_words = %d;\n", word_idx); 160 printf("const unsigned short help_text_words = %d;\n", word_idx);
161 printf("const bool help_valid = true;\n");
161 162
162 return 0; 163 return 0;
163} 164}
diff --git a/apps/plugins/puzzles/dummy/nullhelp.c b/apps/plugins/puzzles/dummy/nullhelp.c
new file mode 100644
index 0000000000..79c36c902b
--- /dev/null
+++ b/apps/plugins/puzzles/dummy/nullhelp.c
@@ -0,0 +1,8 @@
1#include "help.h"
2
3const char help_text[] __attribute__((weak)) = "";
4const char quick_help_text[] __attribute__((weak)) = "";
5const unsigned short help_text_len __attribute__((weak)) = 0, quick_help_text_len __attribute__((weak)) = 0, help_text_words __attribute__((weak)) = 0;
6struct style_text help_text_style[] __attribute__((weak)) = {};
7
8const bool help_valid __attribute__((weak)) = false;
diff --git a/apps/plugins/puzzles/help.h b/apps/plugins/puzzles/help.h
index e9cac9b337..2f870393e8 100644
--- a/apps/plugins/puzzles/help.h
+++ b/apps/plugins/puzzles/help.h
@@ -1,3 +1,5 @@
1#include <stdbool.h>
2
1#ifdef ROCKBOX 3#ifdef ROCKBOX
2#include "lib/display_text.h" 4#include "lib/display_text.h"
3#endif 5#endif
@@ -12,3 +14,5 @@ extern const unsigned short help_text_len, quick_help_text_len, help_text_words;
12#if defined(ROCKBOX) 14#if defined(ROCKBOX)
13extern struct style_text help_text_style[]; 15extern struct style_text help_text_style[];
14#endif 16#endif
17
18extern const bool help_valid;
diff --git a/apps/plugins/puzzles/help/blackbox.c b/apps/plugins/puzzles/help/blackbox.c
index c05daf5fd3..90933604c1 100644
--- a/apps/plugins/puzzles/help/blackbox.c
+++ b/apps/plugins/puzzles/help/blackbox.c
@@ -340,4 +340,5 @@ const char help_text[] = {
340 340
341const unsigned short help_text_len = 5480; 341const unsigned short help_text_len = 5480;
342const unsigned short help_text_words = 1016; 342const unsigned short help_text_words = 1016;
343const bool help_valid = true;
343const char quick_help_text[] = "Find the hidden balls in the box by bouncing laser beams off them."; 344const char quick_help_text[] = "Find the hidden balls in the box by bouncing laser beams off them.";
diff --git a/apps/plugins/puzzles/help/bridges.c b/apps/plugins/puzzles/help/bridges.c
index 3e1c91bcc3..4d42314e4b 100644
--- a/apps/plugins/puzzles/help/bridges.c
+++ b/apps/plugins/puzzles/help/bridges.c
@@ -358,4 +358,5 @@ const char help_text[] = {
358 358
359const unsigned short help_text_len = 5613; 359const unsigned short help_text_len = 5613;
360const unsigned short help_text_words = 1026; 360const unsigned short help_text_words = 1026;
361const bool help_valid = true;
361const char quick_help_text[] = "Connect all the islands with a network of bridges."; 362const char quick_help_text[] = "Connect all the islands with a network of bridges.";
diff --git a/apps/plugins/puzzles/help/cube.c b/apps/plugins/puzzles/help/cube.c
index fbdd380d57..0e2fc94081 100644
--- a/apps/plugins/puzzles/help/cube.c
+++ b/apps/plugins/puzzles/help/cube.c
@@ -166,4 +166,5 @@ const char help_text[] = {
166 166
167const unsigned short help_text_len = 2071; 167const unsigned short help_text_len = 2071;
168const unsigned short help_text_words = 386; 168const unsigned short help_text_words = 386;
169const bool help_valid = true;
169const char quick_help_text[] = "Pick up all the blue squares by rolling the cube over them."; 170const char quick_help_text[] = "Pick up all the blue squares by rolling the cube over them.";
diff --git a/apps/plugins/puzzles/help/dominosa.c b/apps/plugins/puzzles/help/dominosa.c
index 98947a6ee6..94d876794b 100644
--- a/apps/plugins/puzzles/help/dominosa.c
+++ b/apps/plugins/puzzles/help/dominosa.c
@@ -176,4 +176,5 @@ const char help_text[] = {
176 176
177const unsigned short help_text_len = 2299; 177const unsigned short help_text_len = 2299;
178const unsigned short help_text_words = 401; 178const unsigned short help_text_words = 401;
179const bool help_valid = true;
179const char quick_help_text[] = "Tile the rectangle with a full set of dominoes."; 180const char quick_help_text[] = "Tile the rectangle with a full set of dominoes.";
diff --git a/apps/plugins/puzzles/help/fifteen.c b/apps/plugins/puzzles/help/fifteen.c
index 7fc434fc3d..32c8cba399 100644
--- a/apps/plugins/puzzles/help/fifteen.c
+++ b/apps/plugins/puzzles/help/fifteen.c
@@ -152,4 +152,5 @@ const char help_text[] = {
152 152
153const unsigned short help_text_len = 1927; 153const unsigned short help_text_len = 1927;
154const unsigned short help_text_words = 353; 154const unsigned short help_text_words = 353;
155const bool help_valid = true;
155const char quick_help_text[] = "Slide the tiles around to arrange them into order."; 156const char quick_help_text[] = "Slide the tiles around to arrange them into order.";
diff --git a/apps/plugins/puzzles/help/filling.c b/apps/plugins/puzzles/help/filling.c
index c0fe6f47e1..785cfce815 100644
--- a/apps/plugins/puzzles/help/filling.c
+++ b/apps/plugins/puzzles/help/filling.c
@@ -142,4 +142,5 @@ const char help_text[] = {
142 142
143const unsigned short help_text_len = 1821; 143const unsigned short help_text_len = 1821;
144const unsigned short help_text_words = 328; 144const unsigned short help_text_words = 328;
145const bool help_valid = true;
145const char quick_help_text[] = "Mark every square with the area of its containing region."; 146const char quick_help_text[] = "Mark every square with the area of its containing region.";
diff --git a/apps/plugins/puzzles/help/flip.c b/apps/plugins/puzzles/help/flip.c
index fd287cb37b..4f847069cc 100644
--- a/apps/plugins/puzzles/help/flip.c
+++ b/apps/plugins/puzzles/help/flip.c
@@ -131,4 +131,5 @@ const char help_text[] = {
131 131
132const unsigned short help_text_len = 1539; 132const unsigned short help_text_len = 1539;
133const unsigned short help_text_words = 299; 133const unsigned short help_text_words = 299;
134const bool help_valid = true;
134const char quick_help_text[] = "Flip groups of squares to light them all up at once."; 135const char quick_help_text[] = "Flip groups of squares to light them all up at once.";
diff --git a/apps/plugins/puzzles/help/flood.c b/apps/plugins/puzzles/help/flood.c
index 28e18a15a9..ad25a5cf34 100644
--- a/apps/plugins/puzzles/help/flood.c
+++ b/apps/plugins/puzzles/help/flood.c
@@ -182,4 +182,5 @@ const char help_text[] = {
182 182
183const unsigned short help_text_len = 2395; 183const unsigned short help_text_len = 2395;
184const unsigned short help_text_words = 452; 184const unsigned short help_text_words = 452;
185const bool help_valid = true;
185const char quick_help_text[] = "Turn the grid the same colour in as few flood fills as possible."; 186const char quick_help_text[] = "Turn the grid the same colour in as few flood fills as possible.";
diff --git a/apps/plugins/puzzles/help/galaxies.c b/apps/plugins/puzzles/help/galaxies.c
index 8482abc14e..10bcda7b3b 100644
--- a/apps/plugins/puzzles/help/galaxies.c
+++ b/apps/plugins/puzzles/help/galaxies.c
@@ -208,4 +208,5 @@ const char help_text[] = {
208 208
209const unsigned short help_text_len = 2766; 209const unsigned short help_text_len = 2766;
210const unsigned short help_text_words = 498; 210const unsigned short help_text_words = 498;
211const bool help_valid = true;
211const char quick_help_text[] = "Divide the grid into rotationally symmetric regions each centred on a dot."; 212const char quick_help_text[] = "Divide the grid into rotationally symmetric regions each centred on a dot.";
diff --git a/apps/plugins/puzzles/help/guess.c b/apps/plugins/puzzles/help/guess.c
index 594f5910cb..3c77344424 100644
--- a/apps/plugins/puzzles/help/guess.c
+++ b/apps/plugins/puzzles/help/guess.c
@@ -238,4 +238,5 @@ const char help_text[] = {
238 238
239const unsigned short help_text_len = 3506; 239const unsigned short help_text_len = 3506;
240const unsigned short help_text_words = 650; 240const unsigned short help_text_words = 650;
241const bool help_valid = true;
241const char quick_help_text[] = "Guess the hidden combination of colours."; 242const char quick_help_text[] = "Guess the hidden combination of colours.";
diff --git a/apps/plugins/puzzles/help/inertia.c b/apps/plugins/puzzles/help/inertia.c
index 2755cc2eb6..a0f33a9dc3 100644
--- a/apps/plugins/puzzles/help/inertia.c
+++ b/apps/plugins/puzzles/help/inertia.c
@@ -179,4 +179,5 @@ const char help_text[] = {
179 179
180const unsigned short help_text_len = 2286; 180const unsigned short help_text_len = 2286;
181const unsigned short help_text_words = 431; 181const unsigned short help_text_words = 431;
182const bool help_valid = true;
182const char quick_help_text[] = "Collect all the gems without running into any of the mines."; 183const char quick_help_text[] = "Collect all the gems without running into any of the mines.";
diff --git a/apps/plugins/puzzles/help/keen.c b/apps/plugins/puzzles/help/keen.c
index bcf5b38a1f..0c69aea0d6 100644
--- a/apps/plugins/puzzles/help/keen.c
+++ b/apps/plugins/puzzles/help/keen.c
@@ -260,4 +260,5 @@ const char help_text[] = {
260 260
261const unsigned short help_text_len = 3969; 261const unsigned short help_text_len = 3969;
262const unsigned short help_text_words = 762; 262const unsigned short help_text_words = 762;
263const bool help_valid = true;
263const char quick_help_text[] = "Complete the latin square in accordance with the arithmetic clues."; 264const char quick_help_text[] = "Complete the latin square in accordance with the arithmetic clues.";
diff --git a/apps/plugins/puzzles/help/lightup.c b/apps/plugins/puzzles/help/lightup.c
index c3ddd209fa..c109773bf6 100644
--- a/apps/plugins/puzzles/help/lightup.c
+++ b/apps/plugins/puzzles/help/lightup.c
@@ -191,4 +191,5 @@ const char help_text[] = {
191 191
192const unsigned short help_text_len = 2549; 192const unsigned short help_text_len = 2549;
193const unsigned short help_text_words = 468; 193const unsigned short help_text_words = 468;
194const bool help_valid = true;
194const char quick_help_text[] = "Place bulbs to light up all the squares."; 195const char quick_help_text[] = "Place bulbs to light up all the squares.";
diff --git a/apps/plugins/puzzles/help/loopy.c b/apps/plugins/puzzles/help/loopy.c
index f65d2d2793..76c441511c 100644
--- a/apps/plugins/puzzles/help/loopy.c
+++ b/apps/plugins/puzzles/help/loopy.c
@@ -264,4 +264,5 @@ const char help_text[] = {
264 264
265const unsigned short help_text_len = 3584; 265const unsigned short help_text_len = 3584;
266const unsigned short help_text_words = 660; 266const unsigned short help_text_words = 660;
267const bool help_valid = true;
267const char quick_help_text[] = "Draw a single closed loop, given clues about number of adjacent edges."; 268const char quick_help_text[] = "Draw a single closed loop, given clues about number of adjacent edges.";
diff --git a/apps/plugins/puzzles/help/magnets.c b/apps/plugins/puzzles/help/magnets.c
index 9a54586203..aaaa3bcdad 100644
--- a/apps/plugins/puzzles/help/magnets.c
+++ b/apps/plugins/puzzles/help/magnets.c
@@ -190,4 +190,5 @@ const char help_text[] = {
190 190
191const unsigned short help_text_len = 2522; 191const unsigned short help_text_len = 2522;
192const unsigned short help_text_words = 439; 192const unsigned short help_text_words = 439;
193const bool help_valid = true;
193const char quick_help_text[] = "Place magnets to satisfy the clues and avoid like poles touching."; 194const char quick_help_text[] = "Place magnets to satisfy the clues and avoid like poles touching.";
diff --git a/apps/plugins/puzzles/help/map.c b/apps/plugins/puzzles/help/map.c
index 3532ecebbf..05474e3181 100644
--- a/apps/plugins/puzzles/help/map.c
+++ b/apps/plugins/puzzles/help/map.c
@@ -271,4 +271,5 @@ const char help_text[] = {
271 271
272const unsigned short help_text_len = 3752; 272const unsigned short help_text_len = 3752;
273const unsigned short help_text_words = 686; 273const unsigned short help_text_words = 686;
274const bool help_valid = true;
274const char quick_help_text[] = "Colour the map so that adjacent regions are never the same colour."; 275const char quick_help_text[] = "Colour the map so that adjacent regions are never the same colour.";
diff --git a/apps/plugins/puzzles/help/mines.c b/apps/plugins/puzzles/help/mines.c
index 458034ccaa..80cd81ed3f 100644
--- a/apps/plugins/puzzles/help/mines.c
+++ b/apps/plugins/puzzles/help/mines.c
@@ -260,4 +260,5 @@ const char help_text[] = {
260 260
261const unsigned short help_text_len = 3814; 261const unsigned short help_text_len = 3814;
262const unsigned short help_text_words = 732; 262const unsigned short help_text_words = 732;
263const bool help_valid = true;
263const char quick_help_text[] = "Find all the mines without treading on any of them."; 264const char quick_help_text[] = "Find all the mines without treading on any of them.";
diff --git a/apps/plugins/puzzles/help/mosaic.c b/apps/plugins/puzzles/help/mosaic.c
index 9a7d2dd394..eb0c52c0bf 100644
--- a/apps/plugins/puzzles/help/mosaic.c
+++ b/apps/plugins/puzzles/help/mosaic.c
@@ -147,4 +147,5 @@ const char help_text[] = {
147 147
148const unsigned short help_text_len = 1673; 148const unsigned short help_text_len = 1673;
149const unsigned short help_text_words = 285; 149const unsigned short help_text_words = 285;
150const bool help_valid = true;
150const char quick_help_text[] = "Fill in the grid given clues about number of nearby black squares."; 151const char quick_help_text[] = "Fill in the grid given clues about number of nearby black squares.";
diff --git a/apps/plugins/puzzles/help/net.c b/apps/plugins/puzzles/help/net.c
index 83cd785ed2..de528b53bc 100644
--- a/apps/plugins/puzzles/help/net.c
+++ b/apps/plugins/puzzles/help/net.c
@@ -297,4 +297,5 @@ const char help_text[] = {
297 297
298const unsigned short help_text_len = 3919; 298const unsigned short help_text_len = 3919;
299const unsigned short help_text_words = 689; 299const unsigned short help_text_words = 689;
300const bool help_valid = true;
300const char quick_help_text[] = "Rotate each tile to reassemble the network."; 301const char quick_help_text[] = "Rotate each tile to reassemble the network.";
diff --git a/apps/plugins/puzzles/help/netslide.c b/apps/plugins/puzzles/help/netslide.c
index f4067a2303..17ec9ec440 100644
--- a/apps/plugins/puzzles/help/netslide.c
+++ b/apps/plugins/puzzles/help/netslide.c
@@ -58,4 +58,5 @@ const char help_text[] = {
58 58
59const unsigned short help_text_len = 546; 59const unsigned short help_text_len = 546;
60const unsigned short help_text_words = 99; 60const unsigned short help_text_words = 99;
61const bool help_valid = true;
61const char quick_help_text[] = "Slide a row at a time to reassemble the network."; 62const char quick_help_text[] = "Slide a row at a time to reassemble the network.";
diff --git a/apps/plugins/puzzles/help/palisade.c b/apps/plugins/puzzles/help/palisade.c
index d1ee5e7354..36d3c65248 100644
--- a/apps/plugins/puzzles/help/palisade.c
+++ b/apps/plugins/puzzles/help/palisade.c
@@ -140,4 +140,5 @@ const char help_text[] = {
140 140
141const unsigned short help_text_len = 1672; 141const unsigned short help_text_len = 1672;
142const unsigned short help_text_words = 285; 142const unsigned short help_text_words = 285;
143const bool help_valid = true;
143const char quick_help_text[] = "Divide the grid into equal-sized areas in accordance with the clues."; 144const char quick_help_text[] = "Divide the grid into equal-sized areas in accordance with the clues.";
diff --git a/apps/plugins/puzzles/help/pattern.c b/apps/plugins/puzzles/help/pattern.c
index a0e4edc579..fae35c0f64 100644
--- a/apps/plugins/puzzles/help/pattern.c
+++ b/apps/plugins/puzzles/help/pattern.c
@@ -168,4 +168,5 @@ const char help_text[] = {
168 168
169const unsigned short help_text_len = 2167; 169const unsigned short help_text_len = 2167;
170const unsigned short help_text_words = 389; 170const unsigned short help_text_words = 389;
171const bool help_valid = true;
171const char quick_help_text[] = "Fill in the pattern in the grid, given only the lengths of runs of black squares."; 172const char quick_help_text[] = "Fill in the pattern in the grid, given only the lengths of runs of black squares.";
diff --git a/apps/plugins/puzzles/help/pearl.c b/apps/plugins/puzzles/help/pearl.c
index efb3cd0d5a..033aca17fa 100644
--- a/apps/plugins/puzzles/help/pearl.c
+++ b/apps/plugins/puzzles/help/pearl.c
@@ -249,4 +249,5 @@ const char help_text[] = {
249 249
250const unsigned short help_text_len = 3598; 250const unsigned short help_text_len = 3598;
251const unsigned short help_text_words = 659; 251const unsigned short help_text_words = 659;
252const bool help_valid = true;
252const char quick_help_text[] = "Draw a single closed loop, given clues about corner and straight squares."; 253const char quick_help_text[] = "Draw a single closed loop, given clues about corner and straight squares.";
diff --git a/apps/plugins/puzzles/help/pegs.c b/apps/plugins/puzzles/help/pegs.c
index 8375f87bcf..32552a87fd 100644
--- a/apps/plugins/puzzles/help/pegs.c
+++ b/apps/plugins/puzzles/help/pegs.c
@@ -148,4 +148,5 @@ const char help_text[] = {
148 148
149const unsigned short help_text_len = 1734; 149const unsigned short help_text_len = 1734;
150const unsigned short help_text_words = 326; 150const unsigned short help_text_words = 326;
151const bool help_valid = true;
151const char quick_help_text[] = "Jump pegs over each other to remove all but one."; 152const char quick_help_text[] = "Jump pegs over each other to remove all but one.";
diff --git a/apps/plugins/puzzles/help/range.c b/apps/plugins/puzzles/help/range.c
index d5035ef8d2..65495dc3c1 100644
--- a/apps/plugins/puzzles/help/range.c
+++ b/apps/plugins/puzzles/help/range.c
@@ -170,4 +170,5 @@ const char help_text[] = {
170 170
171const unsigned short help_text_len = 2223; 171const unsigned short help_text_len = 2223;
172const unsigned short help_text_words = 395; 172const unsigned short help_text_words = 395;
173const bool help_valid = true;
173const char quick_help_text[] = "Place black squares to limit the visible distance from each numbered cell."; 174const char quick_help_text[] = "Place black squares to limit the visible distance from each numbered cell.";
diff --git a/apps/plugins/puzzles/help/rect.c b/apps/plugins/puzzles/help/rect.c
index bf2197aa47..dfd597eb0f 100644
--- a/apps/plugins/puzzles/help/rect.c
+++ b/apps/plugins/puzzles/help/rect.c
@@ -258,4 +258,5 @@ const char help_text[] = {
258 258
259const unsigned short help_text_len = 3536; 259const unsigned short help_text_len = 3536;
260const unsigned short help_text_words = 603; 260const unsigned short help_text_words = 603;
261const bool help_valid = true;
261const char quick_help_text[] = "Divide the grid into rectangles with areas equal to the numbers."; 262const char quick_help_text[] = "Divide the grid into rectangles with areas equal to the numbers.";
diff --git a/apps/plugins/puzzles/help/samegame.c b/apps/plugins/puzzles/help/samegame.c
index 3c632fca2b..62589b4f11 100644
--- a/apps/plugins/puzzles/help/samegame.c
+++ b/apps/plugins/puzzles/help/samegame.c
@@ -188,4 +188,5 @@ const char help_text[] = {
188 188
189const unsigned short help_text_len = 2492; 189const unsigned short help_text_len = 2492;
190const unsigned short help_text_words = 445; 190const unsigned short help_text_words = 445;
191const bool help_valid = true;
191const char quick_help_text[] = "Clear the grid by removing touching groups of the same colour squares."; 192const char quick_help_text[] = "Clear the grid by removing touching groups of the same colour squares.";
diff --git a/apps/plugins/puzzles/help/signpost.c b/apps/plugins/puzzles/help/signpost.c
index 753a202f3e..34e043da57 100644
--- a/apps/plugins/puzzles/help/signpost.c
+++ b/apps/plugins/puzzles/help/signpost.c
@@ -222,4 +222,5 @@ const char help_text[] = {
222 222
223const unsigned short help_text_len = 3255; 223const unsigned short help_text_len = 3255;
224const unsigned short help_text_words = 595; 224const unsigned short help_text_words = 595;
225const bool help_valid = true;
225const char quick_help_text[] = "Connect the squares into a path following the arrows."; 226const char quick_help_text[] = "Connect the squares into a path following the arrows.";
diff --git a/apps/plugins/puzzles/help/singles.c b/apps/plugins/puzzles/help/singles.c
index a906addeb6..9a1771e3c7 100644
--- a/apps/plugins/puzzles/help/singles.c
+++ b/apps/plugins/puzzles/help/singles.c
@@ -149,4 +149,5 @@ const char help_text[] = {
149 149
150const unsigned short help_text_len = 1780; 150const unsigned short help_text_len = 1780;
151const unsigned short help_text_words = 309; 151const unsigned short help_text_words = 309;
152const bool help_valid = true;
152const char quick_help_text[] = "Black out the right set of duplicate numbers."; 153const char quick_help_text[] = "Black out the right set of duplicate numbers.";
diff --git a/apps/plugins/puzzles/help/sixteen.c b/apps/plugins/puzzles/help/sixteen.c
index 92e64e29bb..1ba92291f3 100644
--- a/apps/plugins/puzzles/help/sixteen.c
+++ b/apps/plugins/puzzles/help/sixteen.c
@@ -197,4 +197,5 @@ const char help_text[] = {
197 197
198const unsigned short help_text_len = 2553; 198const unsigned short help_text_len = 2553;
199const unsigned short help_text_words = 454; 199const unsigned short help_text_words = 454;
200const bool help_valid = true;
200const char quick_help_text[] = "Slide a row at a time to arrange the tiles into order."; 201const char quick_help_text[] = "Slide a row at a time to arrange the tiles into order.";
diff --git a/apps/plugins/puzzles/help/slant.c b/apps/plugins/puzzles/help/slant.c
index f51b141827..7c24a1bac3 100644
--- a/apps/plugins/puzzles/help/slant.c
+++ b/apps/plugins/puzzles/help/slant.c
@@ -199,4 +199,5 @@ const char help_text[] = {
199 199
200const unsigned short help_text_len = 2582; 200const unsigned short help_text_len = 2582;
201const unsigned short help_text_words = 474; 201const unsigned short help_text_words = 474;
202const bool help_valid = true;
202const char quick_help_text[] = "Draw a maze of slanting lines that matches the clues."; 203const char quick_help_text[] = "Draw a maze of slanting lines that matches the clues.";
diff --git a/apps/plugins/puzzles/help/solo.c b/apps/plugins/puzzles/help/solo.c
index cc3d96cf84..68927490e6 100644
--- a/apps/plugins/puzzles/help/solo.c
+++ b/apps/plugins/puzzles/help/solo.c
@@ -383,4 +383,5 @@ const char help_text[] = {
383 383
384const unsigned short help_text_len = 6259; 384const unsigned short help_text_len = 6259;
385const unsigned short help_text_words = 1153; 385const unsigned short help_text_words = 1153;
386const bool help_valid = true;
386const char quick_help_text[] = "Fill in the grid so that each row, column and square block contains one of every digit."; 387const char quick_help_text[] = "Fill in the grid so that each row, column and square block contains one of every digit.";
diff --git a/apps/plugins/puzzles/help/tents.c b/apps/plugins/puzzles/help/tents.c
index c486960ec2..8b14490a51 100644
--- a/apps/plugins/puzzles/help/tents.c
+++ b/apps/plugins/puzzles/help/tents.c
@@ -163,4 +163,5 @@ const char help_text[] = {
163 163
164const unsigned short help_text_len = 2158; 164const unsigned short help_text_len = 2158;
165const unsigned short help_text_words = 401; 165const unsigned short help_text_words = 401;
166const bool help_valid = true;
166const char quick_help_text[] = "Place a tent next to each tree."; 167const char quick_help_text[] = "Place a tent next to each tree.";
diff --git a/apps/plugins/puzzles/help/towers.c b/apps/plugins/puzzles/help/towers.c
index 43f1cef5ea..ff5a0a495a 100644
--- a/apps/plugins/puzzles/help/towers.c
+++ b/apps/plugins/puzzles/help/towers.c
@@ -263,4 +263,5 @@ const char help_text[] = {
263 263
264const unsigned short help_text_len = 3906; 264const unsigned short help_text_len = 3906;
265const unsigned short help_text_words = 732; 265const unsigned short help_text_words = 732;
266const bool help_valid = true;
266const char quick_help_text[] = "Complete the latin square of towers in accordance with the clues."; 267const char quick_help_text[] = "Complete the latin square of towers in accordance with the clues.";
diff --git a/apps/plugins/puzzles/help/tracks.c b/apps/plugins/puzzles/help/tracks.c
index c088145a09..f3677352b5 100644
--- a/apps/plugins/puzzles/help/tracks.c
+++ b/apps/plugins/puzzles/help/tracks.c
@@ -150,4 +150,5 @@ const char help_text[] = {
150 150
151const unsigned short help_text_len = 1881; 151const unsigned short help_text_len = 1881;
152const unsigned short help_text_words = 337; 152const unsigned short help_text_words = 337;
153const bool help_valid = true;
153const char quick_help_text[] = "Fill in the railway track according to the clues."; 154const char quick_help_text[] = "Fill in the railway track according to the clues.";
diff --git a/apps/plugins/puzzles/help/twiddle.c b/apps/plugins/puzzles/help/twiddle.c
index bea3e25ab8..a07ba931d4 100644
--- a/apps/plugins/puzzles/help/twiddle.c
+++ b/apps/plugins/puzzles/help/twiddle.c
@@ -205,4 +205,5 @@ const char help_text[] = {
205 205
206const unsigned short help_text_len = 2945; 206const unsigned short help_text_len = 2945;
207const unsigned short help_text_words = 549; 207const unsigned short help_text_words = 549;
208const bool help_valid = true;
208const char quick_help_text[] = "Rotate the tiles around themselves to arrange them into order."; 209const char quick_help_text[] = "Rotate the tiles around themselves to arrange them into order.";
diff --git a/apps/plugins/puzzles/help/undead.c b/apps/plugins/puzzles/help/undead.c
index 78ecdf386c..dc75e88743 100644
--- a/apps/plugins/puzzles/help/undead.c
+++ b/apps/plugins/puzzles/help/undead.c
@@ -248,4 +248,5 @@ const char help_text[] = {
248 248
249const unsigned short help_text_len = 3574; 249const unsigned short help_text_len = 3574;
250const unsigned short help_text_words = 660; 250const unsigned short help_text_words = 660;
251const bool help_valid = true;
251const char quick_help_text[] = "Place ghosts, vampires and zombies so that the right numbers of them can be seen in mirrors."; 252const char quick_help_text[] = "Place ghosts, vampires and zombies so that the right numbers of them can be seen in mirrors.";
diff --git a/apps/plugins/puzzles/help/unequal.c b/apps/plugins/puzzles/help/unequal.c
index 563f9d113b..6bccf6bef3 100644
--- a/apps/plugins/puzzles/help/unequal.c
+++ b/apps/plugins/puzzles/help/unequal.c
@@ -257,4 +257,5 @@ const char help_text[] = {
257 257
258const unsigned short help_text_len = 3954; 258const unsigned short help_text_len = 3954;
259const unsigned short help_text_words = 731; 259const unsigned short help_text_words = 731;
260const bool help_valid = true;
260const char quick_help_text[] = "Complete the latin square in accordance with the > signs."; 261const char quick_help_text[] = "Complete the latin square in accordance with the > signs.";
diff --git a/apps/plugins/puzzles/help/unruly.c b/apps/plugins/puzzles/help/unruly.c
index dafb3274db..97348e5c26 100644
--- a/apps/plugins/puzzles/help/unruly.c
+++ b/apps/plugins/puzzles/help/unruly.c
@@ -145,4 +145,5 @@ const char help_text[] = {
145 145
146const unsigned short help_text_len = 1707; 146const unsigned short help_text_len = 1707;
147const unsigned short help_text_words = 306; 147const unsigned short help_text_words = 306;
148const bool help_valid = true;
148const char quick_help_text[] = "Fill in the black and white grid to avoid runs of three."; 149const char quick_help_text[] = "Fill in the black and white grid to avoid runs of three.";
diff --git a/apps/plugins/puzzles/help/untangle.c b/apps/plugins/puzzles/help/untangle.c
index cdf9b96d25..88b6e39d5a 100644
--- a/apps/plugins/puzzles/help/untangle.c
+++ b/apps/plugins/puzzles/help/untangle.c
@@ -97,4 +97,5 @@ const char help_text[] = {
97 97
98const unsigned short help_text_len = 974; 98const unsigned short help_text_len = 974;
99const unsigned short help_text_words = 174; 99const unsigned short help_text_words = 174;
100const bool help_valid = true;
100const char quick_help_text[] = "Reposition the points so that the lines do not cross."; 101const char quick_help_text[] = "Reposition the points so that the lines do not cross.";
diff --git a/apps/plugins/puzzles/puzzles.make b/apps/plugins/puzzles/puzzles.make
index 604208cbdd..8c5bc1de40 100644
--- a/apps/plugins/puzzles/puzzles.make
+++ b/apps/plugins/puzzles/puzzles.make
@@ -25,6 +25,8 @@ PUZZLES_OBJ = $(call c2obj, $(PUZZLES_SRC))
25PUZZLES_ROCKS = $(addprefix $(PUZZLES_OBJDIR)/sgt-, $(notdir $(PUZZLES_GAMES_SRC:.c=.rock))) 25PUZZLES_ROCKS = $(addprefix $(PUZZLES_OBJDIR)/sgt-, $(notdir $(PUZZLES_GAMES_SRC:.c=.rock)))
26 26
27OTHER_SRC += $(PUZZLES_SRC) 27OTHER_SRC += $(PUZZLES_SRC)
28OTHER_INC += -I$(PUZZLES_SRCDIR)/src -I $(PUZZLES_SRCDIR)
29
28ROCKS += $(PUZZLES_ROCKS) 30ROCKS += $(PUZZLES_ROCKS)
29 31
30PUZZLES_OPTIMIZE = -O2 32PUZZLES_OPTIMIZE = -O2
@@ -49,6 +51,13 @@ $(PUZZLES_OBJDIR)/sgt-%.rock: $(PUZZLES_OBJDIR)/src/%.o $(PUZZLES_OBJDIR)/help/%
49 -lgcc $(filter-out -Wl%.map, $(PLUGINLDFLAGS)) -Wl,-Map,$(PUZZLES_OBJDIR)/src/$*.map 51 -lgcc $(filter-out -Wl%.map, $(PLUGINLDFLAGS)) -Wl,-Map,$(PUZZLES_OBJDIR)/src/$*.map
50 $(SILENT)$(call objcopy,$(PUZZLES_OBJDIR)/$*.elf,$@) 52 $(SILENT)$(call objcopy,$(PUZZLES_OBJDIR)/$*.elf,$@)
51 53
54$(PUZZLES_OBJDIR)/sgt-%.rock: $(PUZZLES_OBJDIR)/src/unfinished/%.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
55 $(call PRINTS,LD $(@F))$(CC) $(PLUGINFLAGS) -o $(PUZZLES_OBJDIR)/$*.elf \
56 $(filter %.o, $^) \
57 $(filter %.a, $+) \
58 -lgcc $(filter-out -Wl%.map, $(PLUGINLDFLAGS)) -Wl,-Map,$(PUZZLES_OBJDIR)/src/$*.map
59 $(SILENT)$(call objcopy,$(PUZZLES_OBJDIR)/$*.elf,$@)
60
52$(PUZZLES_SRCDIR)/rbcompat.h: $(APPSDIR)/plugin.h \ 61$(PUZZLES_SRCDIR)/rbcompat.h: $(APPSDIR)/plugin.h \
53 $(APPSDIR)/plugins/lib/pluginlib_exit.h \ 62 $(APPSDIR)/plugins/lib/pluginlib_exit.h \
54 $(BUILDDIR)/sysfont.h \ 63 $(BUILDDIR)/sysfont.h \
diff --git a/apps/plugins/puzzles/resync.sh b/apps/plugins/puzzles/resync.sh
index 7c2df45c7e..3431a6f695 100755
--- a/apps/plugins/puzzles/resync.sh
+++ b/apps/plugins/puzzles/resync.sh
@@ -30,8 +30,8 @@ then
30 echo "[1/5] Removing current src/ directory" 30 echo "[1/5] Removing current src/ directory"
31 rm -rf src 31 rm -rf src
32 echo "[2/5] Copying new sources" 32 echo "[2/5] Copying new sources"
33 mkdir src 33 mkdir -p src/unfinished
34 cp -r "$1"/{*.h,puzzles.but,LICENCE,README,CMakeLists.txt} src 34 cp -r "$1"/{*.h,puzzles.but,LICENCE,README,CMakeLists.txt,unfinished} src
35 35
36 # Parse out definitions of core, core_obj, and common from 36 # Parse out definitions of core, core_obj, and common from
37 # CMakeLists. Extract the .c filenames, except malloc.c, and store 37 # CMakeLists. Extract the .c filenames, except malloc.c, and store
@@ -46,17 +46,12 @@ then
46 SRC="$(cat SOURCES.games SOURCES.core | sed 's/src\///' | tr '\n' ' ' | head -c-1) loopy.c pearl.c solo.c" 46 SRC="$(cat SOURCES.games SOURCES.core | sed 's/src\///' | tr '\n' ' ' | head -c-1) loopy.c pearl.c solo.c"
47 echo "Detected sources:" $SRC 47 echo "Detected sources:" $SRC
48 pushd "$1" > /dev/null 48 pushd "$1" > /dev/null
49 cp $SRC "$ROOT"/src 49 cp -r $SRC "$ROOT"/src
50 popd > /dev/null 50 popd > /dev/null
51 51
52 cat <<EOF >> SOURCES.games 52 cat src/unfinished/CMakeLists.txt | awk '/puzzle\(/{p=1} p{print} /\)/{p=0}' | grep -Eo "\(.*$" | tr -dc "a-z\n" | awk '{print "src/unfinished/"$0".c"}' | grep -v "group" | grep -v "separate" >> SOURCES.games
53 53
54/* Disabled for now. Fix puzzles.make and CATEGORIES to accomodate these. */ 54 cat <<EOF >> SOURCES.games
55/* The help system would also need to be patched to compile these. */
56/*src/unfinished/group.c*/
57/*src/unfinished/separate.c*/
58/*src/unfinished/slide.c*/
59/*src/unfinished/sokoban.c*/
60 55
61/* no c200v2 */ 56/* no c200v2 */
62#if PLUGIN_BUFFER_SIZE > 0x14000 57#if PLUGIN_BUFFER_SIZE > 0x14000
diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c
index 27005a447d..27060208fc 100644
--- a/apps/plugins/puzzles/rockbox.c
+++ b/apps/plugins/puzzles/rockbox.c
@@ -322,6 +322,7 @@ static struct viewport clip_rect;
322static bool clipped = false, zoom_enabled = false, view_mode = true, mouse_mode = false; 322static bool clipped = false, zoom_enabled = false, view_mode = true, mouse_mode = false;
323 323
324static int mouse_x, mouse_y; 324static int mouse_x, mouse_y;
325static bool mouse_dragging = false; /* for sticky mode only */
325 326
326extern bool audiobuf_available; /* defined in rbmalloc.c */ 327extern bool audiobuf_available; /* defined in rbmalloc.c */
327 328
@@ -346,6 +347,7 @@ static struct {
346 bool ignore_repeats; /* ignore repeated button events (currently in all games but Untangle) */ 347 bool ignore_repeats; /* ignore repeated button events (currently in all games but Untangle) */
347 bool rclick_on_hold; /* if in mouse mode, send right-click on long-press of select */ 348 bool rclick_on_hold; /* if in mouse mode, send right-click on long-press of select */
348 bool numerical_chooser; /* repurpose select to activate a numerical chooser */ 349 bool numerical_chooser; /* repurpose select to activate a numerical chooser */
350 bool sticky_mouse; /* if mouse left button should be persistent and toggled on/off */
349} input_settings; 351} input_settings;
350 352
351static bool accept_input = true; 353static bool accept_input = true;
@@ -748,7 +750,8 @@ static void rb_color(int n)
748 fatal("bad color %d", n); 750 fatal("bad color %d", n);
749 return; 751 return;
750 } 752 }
751 rb->lcd_set_foreground(colors[n]); 753 if(colors)
754 rb->lcd_set_foreground(colors[n]);
752} 755}
753 756
754/* clipping is implemented through viewports and offsetting 757/* clipping is implemented through viewports and offsetting
@@ -1284,7 +1287,8 @@ static void draw_title(bool clear_first)
1284 rb->lcd_setfont(cur_font = FONT_UI); 1287 rb->lcd_setfont(cur_font = FONT_UI);
1285 rb->lcd_getstringsize(str, &w, &h); 1288 rb->lcd_getstringsize(str, &w, &h);
1286 1289
1287 rb->lcd_set_foreground(BG_COLOR); 1290
1291 rb->lcd_set_foreground(colors ? colors[0] : BG_COLOR);
1288 rb->lcd_fillrect(0, LCD_HEIGHT - h, clear_first ? LCD_WIDTH : w, h); 1292 rb->lcd_fillrect(0, LCD_HEIGHT - h, clear_first ? LCD_WIDTH : w, h);
1289 1293
1290 rb->lcd_set_drawmode(DRMODE_FG); 1294 rb->lcd_set_drawmode(DRMODE_FG);
@@ -1682,9 +1686,17 @@ static int process_input(int tmo, bool do_pausemenu)
1682 LOGF("sending left click"); 1686 LOGF("sending left click");
1683 send_click(LEFT_BUTTON, true); /* right-click is handled earlier */ 1687 send_click(LEFT_BUTTON, true); /* right-click is handled earlier */
1684 } 1688 }
1685 } 1689 } else if(input_settings.sticky_mouse) {
1686 else 1690 if(pressed & BTN_FIRE) {
1687 { 1691 send_click(LEFT_BUTTON, false);
1692 accept_input = false;
1693 mouse_dragging = !mouse_dragging;
1694 } else if(mouse_dragging) {
1695 send_click(LEFT_DRAG, false);
1696 } else {
1697 send_click(LEFT_RELEASE, false);
1698 }
1699 } else {
1688 if(pressed & BTN_FIRE) { 1700 if(pressed & BTN_FIRE) {
1689 send_click(LEFT_BUTTON, false); 1701 send_click(LEFT_BUTTON, false);
1690 accept_input = false; 1702 accept_input = false;
@@ -2482,6 +2494,7 @@ static bool presets_menu(void)
2482 2494
2483static void quick_help(void) 2495static void quick_help(void)
2484{ 2496{
2497#ifndef NO_HELP_TEXT
2485#if defined(FOR_REAL) && defined(DEBUG_MENU) 2498#if defined(FOR_REAL) && defined(DEBUG_MENU)
2486 if(++help_times >= 5) 2499 if(++help_times >= 5)
2487 { 2500 {
@@ -2492,11 +2505,12 @@ static void quick_help(void)
2492 2505
2493 rb->splash(0, quick_help_text); 2506 rb->splash(0, quick_help_text);
2494 rb->button_get(true); 2507 rb->button_get(true);
2495 return; 2508#endif
2496} 2509}
2497 2510
2498static void full_help(const char *name) 2511static void full_help(const char *name)
2499{ 2512{
2513#ifndef NO_HELP_TEXT
2500 unsigned old_bg = rb->lcd_get_background(); 2514 unsigned old_bg = rb->lcd_get_background();
2501 2515
2502 bool orig_clipped = clipped; 2516 bool orig_clipped = clipped;
@@ -2551,6 +2565,7 @@ static void full_help(const char *name)
2551 2565
2552 if(orig_clipped) 2566 if(orig_clipped)
2553 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height); 2567 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height);
2568#endif
2554} 2569}
2555 2570
2556static void init_default_settings(void) 2571static void init_default_settings(void)
@@ -2701,6 +2716,11 @@ static int pausemenu_cb(int action,
2701 if(!midend_which_game(me)->can_solve) 2716 if(!midend_which_game(me)->can_solve)
2702 return ACTION_EXIT_MENUITEM; 2717 return ACTION_EXIT_MENUITEM;
2703 break; 2718 break;
2719 case 7:
2720 case 8:
2721 if(!help_valid)
2722 return ACTION_EXIT_MENUITEM;
2723 break;
2704 case 9: 2724 case 9:
2705 if(audiobuf_available) 2725 if(audiobuf_available)
2706 break; 2726 break;
@@ -2751,7 +2771,7 @@ static void reset_drawing(void)
2751 rb->lcd_set_viewport(NULL); 2771 rb->lcd_set_viewport(NULL);
2752 rb->lcd_set_backdrop(NULL); 2772 rb->lcd_set_backdrop(NULL);
2753 rb->lcd_set_foreground(LCD_BLACK); 2773 rb->lcd_set_foreground(LCD_BLACK);
2754 rb->lcd_set_background(BG_COLOR); 2774 rb->lcd_set_background(colors ? colors[0] : BG_COLOR);
2755} 2775}
2756 2776
2757/* Make a new game, but tell the user through a splash so they don't 2777/* Make a new game, but tell the user through a splash so they don't
@@ -2876,7 +2896,7 @@ static int pause_menu(void)
2876 break; 2896 break;
2877 } 2897 }
2878 } 2898 }
2879 rb->lcd_set_background(BG_COLOR); 2899 rb->lcd_set_background(colors ? colors[0] : BG_COLOR);
2880 rb->lcd_clear_display(); 2900 rb->lcd_clear_display();
2881 midend_force_redraw(me); 2901 midend_force_redraw(me);
2882 rb->lcd_update(); 2902 rb->lcd_update();
@@ -2923,6 +2943,7 @@ static void init_colors(void)
2923 float *floatcolors = midend_colors(me, &ncolors); 2943 float *floatcolors = midend_colors(me, &ncolors);
2924 2944
2925 /* convert them to packed RGB */ 2945 /* convert them to packed RGB */
2946 sfree(colors);
2926 colors = smalloc(ncolors * sizeof(unsigned)); 2947 colors = smalloc(ncolors * sizeof(unsigned));
2927 unsigned *ptr = colors; 2948 unsigned *ptr = colors;
2928 float *floatptr = floatcolors; 2949 float *floatptr = floatcolors;
@@ -3007,6 +3028,7 @@ static void tune_input(const char *name)
3007 static const char *no_rclick_on_hold[] = { 3028 static const char *no_rclick_on_hold[] = {
3008 "Map", 3029 "Map",
3009 "Signpost", 3030 "Signpost",
3031 "Slide",
3010 "Untangle", 3032 "Untangle",
3011 NULL 3033 NULL
3012 }; 3034 };
@@ -3015,11 +3037,21 @@ static void tune_input(const char *name)
3015 3037
3016 static const char *mouse_games[] = { 3038 static const char *mouse_games[] = {
3017 "Loopy", 3039 "Loopy",
3040 "Slide",
3018 NULL 3041 NULL
3019 }; 3042 };
3020 3043
3021 mouse_mode = string_in_list(name, mouse_games); 3044 mouse_mode = string_in_list(name, mouse_games);
3022 3045
3046 static const char *sticky_mouse_games[] = {
3047 "Map",
3048 "Signpost",
3049 "Slide",
3050 "Untangle",
3051 };
3052
3053 input_settings.sticky_mouse = string_in_list(name, sticky_mouse_games);
3054
3023 static const char *number_chooser_games[] = { 3055 static const char *number_chooser_games[] = {
3024 "Filling", 3056 "Filling",
3025 "Keen", 3057 "Keen",
@@ -3312,8 +3344,11 @@ static int mainmenu_cb(int action,
3312 if(!load_success) 3344 if(!load_success)
3313 return ACTION_EXIT_MENUITEM; 3345 return ACTION_EXIT_MENUITEM;
3314 break; 3346 break;
3347 case 2:
3315 case 3: 3348 case 3:
3316 break; 3349 if(!help_valid)
3350 return ACTION_EXIT_MENUITEM;
3351 break;
3317 case 4: 3352 case 4:
3318 if(audiobuf_available) 3353 if(audiobuf_available)
3319 break; 3354 break;
@@ -3476,12 +3511,14 @@ static void puzzles_main(void)
3476 /* quit without saving */ 3511 /* quit without saving */
3477 midend_free(me); 3512 midend_free(me);
3478 sfree(colors); 3513 sfree(colors);
3514 colors = NULL;
3479 return; 3515 return;
3480 case -3: 3516 case -3:
3481 /* save and quit */ 3517 /* save and quit */
3482 save_game(); 3518 save_game();
3483 midend_free(me); 3519 midend_free(me);
3484 sfree(colors); 3520 sfree(colors);
3521 colors = NULL;
3485 return; 3522 return;
3486 default: 3523 default:
3487 break; 3524 break;
@@ -3511,6 +3548,7 @@ static void puzzles_main(void)
3511 rb->yield(); 3548 rb->yield();
3512 } 3549 }
3513 sfree(colors); 3550 sfree(colors);
3551 colors = NULL;
3514 } 3552 }
3515} 3553}
3516 3554
diff --git a/apps/plugins/puzzles/src/unfinished/CMakeLists.txt b/apps/plugins/puzzles/src/unfinished/CMakeLists.txt
new file mode 100644
index 0000000000..0c1e331f9b
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/CMakeLists.txt
@@ -0,0 +1,31 @@
1puzzle(group
2 DISPLAYNAME "Group"
3 DESCRIPTION "Group theory puzzle"
4 OBJECTIVE "Complete the unfinished Cayley table of a group.")
5solver(group ${CMAKE_SOURCE_DIR}/latin.c)
6
7puzzle(separate
8 DISPLAYNAME "Separate"
9 DESCRIPTION "Rectangle-dividing puzzle"
10 OBJECTIVE "Partition the grid into regions containing one of each letter.")
11
12puzzle(slide
13 DISPLAYNAME "Slide"
14 DESCRIPTION "Sliding block puzzle"
15 OBJECTIVE "Slide the blocks to let the key block out.")
16solver(slide)
17
18puzzle(sokoban
19 DISPLAYNAME "Sokoban"
20 DESCRIPTION "Barrel-pushing puzzle"
21 OBJECTIVE "Push all the barrels into the target squares.")
22
23# These unfinished programs don't even have the structure of a puzzle
24# game yet; they're just command-line programs containing test
25# implementations of some of the needed functionality.
26
27cliprogram(numgame numgame.c)
28
29cliprogram(path path.c COMPILE_DEFINITIONS TEST_GEN)
30
31export_variables_to_parent_scope()
diff --git a/apps/plugins/puzzles/src/unfinished/README b/apps/plugins/puzzles/src/unfinished/README
new file mode 100644
index 0000000000..c96ccc935a
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/README
@@ -0,0 +1,14 @@
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
5The CMake build system will _build_ all of the source in this
6directory (to ensure it hasn't become unbuildable), but they won't be
7included in all-in-one puzzle binaries or installed by 'make install'
8targets. If you want to temporarily change that, you can reconfigure
9your build by defining the CMake variable PUZZLES_ENABLE_UNFINISHED.
10For example,
11
12 cmake . -DPUZZLES_ENABLE_UNFINISHED="group;slide"
13
14will build as if both Group and Slide were fully official puzzles.
diff --git a/apps/plugins/puzzles/src/unfinished/group.c b/apps/plugins/puzzles/src/unfinished/group.c
new file mode 100644
index 0000000000..faffa89485
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/group.c
@@ -0,0 +1,2497 @@
1/*
2 * group.c: a Latin-square puzzle, but played with groups' Cayley
3 * tables. That is, you are given a Cayley table of a group with
4 * most elements blank and a few clues, and you must fill it in
5 * so as to preserve the group axioms.
6 *
7 * This is a perfectly playable and fully working puzzle, but I'm
8 * leaving it for the moment in the 'unfinished' directory because
9 * it's just too esoteric (not to mention _hard_) for me to be
10 * comfortable presenting it to the general public as something they
11 * might (implicitly) actually want to play.
12 *
13 * TODO:
14 *
15 * - more solver techniques?
16 * * Inverses: once we know that gh = e, we can immediately
17 * deduce hg = e as well; then for any gx=y we can deduce
18 * hy=x, and for any xg=y we have yh=x.
19 * * Hard-mode associativity: we currently deduce based on
20 * definite numbers in the grid, but we could also winnow
21 * based on _possible_ numbers.
22 * * My overambitious original thoughts included wondering if we
23 * could infer that there must be elements of certain orders
24 * (e.g. a group of order divisible by 5 must contain an
25 * element of order 5), but I think in fact this is probably
26 * silly.
27 */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <assert.h>
33#include <ctype.h>
34#ifdef NO_TGMATH_H
35# include <math.h>
36#else
37# include <tgmath.h>
38#endif
39
40#include "puzzles.h"
41#include "latin.h"
42
43/*
44 * Difficulty levels. I do some macro ickery here to ensure that my
45 * enum and the various forms of my name list always match up.
46 */
47#define DIFFLIST(A) \
48 A(TRIVIAL,Trivial,NULL,t) \
49 A(NORMAL,Normal,solver_normal,n) \
50 A(HARD,Hard,solver_hard,h) \
51 A(EXTREME,Extreme,NULL,x) \
52 A(UNREASONABLE,Unreasonable,NULL,u)
53#define ENUM(upper,title,func,lower) DIFF_ ## upper,
54#define TITLE(upper,title,func,lower) #title,
55#define ENCODE(upper,title,func,lower) #lower
56#define CONFIG(upper,title,func,lower) ":" #title
57enum { DIFFLIST(ENUM) DIFFCOUNT };
58static char const *const group_diffnames[] = { DIFFLIST(TITLE) };
59static char const group_diffchars[] = DIFFLIST(ENCODE);
60#define DIFFCONFIG DIFFLIST(CONFIG)
61
62enum {
63 COL_BACKGROUND,
64 COL_GRID,
65 COL_USER,
66 COL_HIGHLIGHT,
67 COL_ERROR,
68 COL_PENCIL,
69 COL_DIAGONAL,
70 NCOLOURS
71};
72
73/*
74 * In identity mode, we number the elements e,a,b,c,d,f,g,h,...
75 * Otherwise, they're a,b,c,d,e,f,g,h,... in the obvious way.
76 */
77#define E_TO_FRONT(c,id) ( (id) && (c)<=5 ? (c) % 5 + 1 : (c) )
78#define E_FROM_FRONT(c,id) ( (id) && (c)<=5 ? ((c) + 3) % 5 + 1 : (c) )
79
80#define FROMCHAR(c,id) E_TO_FRONT((((c)-('A'-1)) & ~0x20), id)
81#define ISCHAR(c) (((c)>='A'&&(c)<='Z') || ((c)>='a'&&(c)<='z'))
82#define TOCHAR(c,id) (E_FROM_FRONT(c,id) + ('a'-1))
83
84struct game_params {
85 int w, diff;
86 bool id;
87};
88
89typedef struct group_common {
90 int refcount;
91 bool *immutable;
92} group_common;
93
94struct game_state {
95 game_params par;
96 digit *grid;
97 int *pencil; /* bitmaps using bits 1<<1..1<<n */
98 group_common *common;
99 bool completed, cheated;
100 digit *sequence; /* sequence of group elements shown */
101
102 /*
103 * This array indicates thick lines separating rows and columns
104 * placed and unplaced manually by the user as a visual aid, e.g.
105 * to delineate a subgroup and its cosets.
106 *
107 * When a line is placed, it's deemed to be between the two
108 * particular group elements that are on either side of it at the
109 * time; dragging those two away from each other automatically
110 * gets rid of the line. Hence, for a given element i, dividers[i]
111 * is either -1 (indicating no divider to the right of i), or some
112 * other element (indicating a divider to the right of i iff that
113 * element is the one right of it). These are eagerly cleared
114 * during drags.
115 */
116 int *dividers; /* thick lines between rows/cols */
117};
118
119static game_params *default_params(void)
120{
121 game_params *ret = snew(game_params);
122
123 ret->w = 6;
124 ret->diff = DIFF_NORMAL;
125 ret->id = true;
126
127 return ret;
128}
129
130static const struct game_params group_presets[] = {
131 { 6, DIFF_NORMAL, true },
132 { 6, DIFF_NORMAL, false },
133 { 8, DIFF_NORMAL, true },
134 { 8, DIFF_NORMAL, false },
135 { 8, DIFF_HARD, true },
136 { 8, DIFF_HARD, false },
137 { 12, DIFF_NORMAL, true },
138};
139
140static bool game_fetch_preset(int i, char **name, game_params **params)
141{
142 game_params *ret;
143 char buf[80];
144
145 if (i < 0 || i >= lenof(group_presets))
146 return false;
147
148 ret = snew(game_params);
149 *ret = group_presets[i]; /* structure copy */
150
151 sprintf(buf, "%dx%d %s%s", ret->w, ret->w, group_diffnames[ret->diff],
152 ret->id ? "" : ", identity hidden");
153
154 *name = dupstr(buf);
155 *params = ret;
156 return true;
157}
158
159static void free_params(game_params *params)
160{
161 sfree(params);
162}
163
164static game_params *dup_params(const game_params *params)
165{
166 game_params *ret = snew(game_params);
167 *ret = *params; /* structure copy */
168 return ret;
169}
170
171static void decode_params(game_params *params, char const *string)
172{
173 char const *p = string;
174
175 params->w = atoi(p);
176 while (*p && isdigit((unsigned char)*p)) p++;
177 params->diff = DIFF_NORMAL;
178 params->id = true;
179
180 while (*p) {
181 if (*p == 'd') {
182 int i;
183 p++;
184 params->diff = DIFFCOUNT+1; /* ...which is invalid */
185 if (*p) {
186 for (i = 0; i < DIFFCOUNT; i++) {
187 if (*p == group_diffchars[i])
188 params->diff = i;
189 }
190 p++;
191 }
192 } else if (*p == 'i') {
193 params->id = false;
194 p++;
195 } else {
196 /* unrecognised character */
197 p++;
198 }
199 }
200}
201
202static char *encode_params(const game_params *params, bool full)
203{
204 char ret[80];
205
206 sprintf(ret, "%d", params->w);
207 if (full)
208 sprintf(ret + strlen(ret), "d%c", group_diffchars[params->diff]);
209 if (!params->id)
210 sprintf(ret + strlen(ret), "i");
211
212 return dupstr(ret);
213}
214
215static config_item *game_configure(const game_params *params)
216{
217 config_item *ret;
218 char buf[80];
219
220 ret = snewn(4, config_item);
221
222 ret[0].name = "Grid size";
223 ret[0].type = C_STRING;
224 sprintf(buf, "%d", params->w);
225 ret[0].u.string.sval = dupstr(buf);
226
227 ret[1].name = "Difficulty";
228 ret[1].type = C_CHOICES;
229 ret[1].u.choices.choicenames = DIFFCONFIG;
230 ret[1].u.choices.selected = params->diff;
231
232 ret[2].name = "Show identity";
233 ret[2].type = C_BOOLEAN;
234 ret[2].u.boolean.bval = params->id;
235
236 ret[3].name = NULL;
237 ret[3].type = C_END;
238
239 return ret;
240}
241
242static game_params *custom_params(const config_item *cfg)
243{
244 game_params *ret = snew(game_params);
245
246 ret->w = atoi(cfg[0].u.string.sval);
247 ret->diff = cfg[1].u.choices.selected;
248 ret->id = cfg[2].u.boolean.bval;
249
250 return ret;
251}
252
253static const char *validate_params(const game_params *params, bool full)
254{
255 if (params->w < 3 || params->w > 26)
256 return "Grid size must be between 3 and 26";
257 if (params->diff >= DIFFCOUNT)
258 return "Unknown difficulty rating";
259 if (!params->id && params->diff == DIFF_TRIVIAL) {
260 /*
261 * We can't have a Trivial-difficulty puzzle (i.e. latin
262 * square deductions only) without a clear identity, because
263 * identityless puzzles always have two rows and two columns
264 * entirely blank, and no latin-square deduction permits the
265 * distinguishing of two such rows.
266 */
267 return "Trivial puzzles must have an identity";
268 }
269 if (!params->id && params->w == 3) {
270 /*
271 * We can't have a 3x3 puzzle without an identity either,
272 * because 3x3 puzzles can't ever be harder than Trivial
273 * (there are no 3x3 latin squares which aren't also valid
274 * group tables, so enabling group-based deductions doesn't
275 * rule out any possible solutions) and - as above - Trivial
276 * puzzles can't not have an identity.
277 */
278 return "3x3 puzzles must have an identity";
279 }
280 return NULL;
281}
282
283/* ----------------------------------------------------------------------
284 * Solver.
285 */
286
287static int find_identity(struct latin_solver *solver)
288{
289 int w = solver->o;
290 digit *grid = solver->grid;
291 int i, j;
292
293 for (i = 0; i < w; i++)
294 for (j = 0; j < w; j++) {
295 if (grid[i*w+j] == i+1)
296 return j+1;
297 if (grid[i*w+j] == j+1)
298 return i+1;
299 }
300
301 return 0;
302}
303
304static int solver_normal(struct latin_solver *solver, void *vctx)
305{
306 int w = solver->o;
307#ifdef STANDALONE_SOLVER
308 char **names = solver->names;
309#endif
310 digit *grid = solver->grid;
311 int i, j, k;
312
313 /*
314 * Deduce using associativity: (ab)c = a(bc).
315 *
316 * So we pick any a,b,c we like; then if we know ab, bc, and
317 * (ab)c we can fill in a(bc).
318 */
319 for (i = 0; i < w; i++)
320 for (j = 0; j < w; j++)
321 for (k = 0; k < w; k++) {
322 if (!grid[i*w+j] || !grid[j*w+k])
323 continue;
324 if (grid[(grid[i*w+j]-1)*w+k] &&
325 !grid[i*w+(grid[j*w+k]-1)]) {
326 int x = grid[j*w+k]-1, y = i;
327 int n = grid[(grid[i*w+j]-1)*w+k];
328#ifdef STANDALONE_SOLVER
329 if (solver_show_working) {
330 printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n",
331 solver_recurse_depth*4, "",
332 names[i], names[j], names[k],
333 names[grid[i*w+j]-1], names[k],
334 names[i], names[grid[j*w+k]-1]);
335 printf("%*s placing %s at (%d,%d)\n",
336 solver_recurse_depth*4, "",
337 names[n-1], x+1, y+1);
338 }
339#endif
340 if (solver->cube[(x*w+y)*w+n-1]) {
341 latin_solver_place(solver, x, y, n);
342 return 1;
343 } else {
344#ifdef STANDALONE_SOLVER
345 if (solver_show_working)
346 printf("%*s contradiction!\n",
347 solver_recurse_depth*4, "");
348 return -1;
349#endif
350 }
351 }
352 if (!grid[(grid[i*w+j]-1)*w+k] &&
353 grid[i*w+(grid[j*w+k]-1)]) {
354 int x = k, y = grid[i*w+j]-1;
355 int n = grid[i*w+(grid[j*w+k]-1)];
356#ifdef STANDALONE_SOLVER
357 if (solver_show_working) {
358 printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n",
359 solver_recurse_depth*4, "",
360 names[i], names[j], names[k],
361 names[grid[i*w+j]-1], names[k],
362 names[i], names[grid[j*w+k]-1]);
363 printf("%*s placing %s at (%d,%d)\n",
364 solver_recurse_depth*4, "",
365 names[n-1], x+1, y+1);
366 }
367#endif
368 if (solver->cube[(x*w+y)*w+n-1]) {
369 latin_solver_place(solver, x, y, n);
370 return 1;
371 } else {
372#ifdef STANDALONE_SOLVER
373 if (solver_show_working)
374 printf("%*s contradiction!\n",
375 solver_recurse_depth*4, "");
376 return -1;
377#endif
378 }
379 }
380 }
381
382 /*
383 * Fill in the row and column for the group identity, if it's not
384 * already known and if we've just found out what it is.
385 */
386 i = find_identity(solver);
387 if (i) {
388 bool done_something = false;
389 for (j = 1; j <= w; j++) {
390 if (!grid[(i-1)*w+(j-1)] || !grid[(j-1)*w+(i-1)]) {
391 done_something = true;
392 }
393 }
394 if (done_something) {
395#ifdef STANDALONE_SOLVER
396 if (solver_show_working) {
397 printf("%*s%s is the group identity\n",
398 solver_recurse_depth*4, "", names[i-1]);
399 }
400#endif
401 for (j = 1; j <= w; j++) {
402 if (!grid[(j-1)*w+(i-1)]) {
403 if (!cube(i-1, j-1, j)) {
404#ifdef STANDALONE_SOLVER
405 if (solver_show_working) {
406 printf("%*s but %s cannot go at (%d,%d) - "
407 "contradiction!\n",
408 solver_recurse_depth*4, "",
409 names[j-1], i, j);
410 }
411#endif
412 return -1;
413 }
414#ifdef STANDALONE_SOLVER
415 if (solver_show_working) {
416 printf("%*s placing %s at (%d,%d)\n",
417 solver_recurse_depth*4, "",
418 names[j-1], i, j);
419 }
420#endif
421 latin_solver_place(solver, i-1, j-1, j);
422 }
423 if (!grid[(i-1)*w+(j-1)]) {
424 if (!cube(j-1, i-1, j)) {
425#ifdef STANDALONE_SOLVER
426 if (solver_show_working) {
427 printf("%*s but %s cannot go at (%d,%d) - "
428 "contradiction!\n",
429 solver_recurse_depth*4, "",
430 names[j-1], j, i);
431 }
432#endif
433 return -1;
434 }
435#ifdef STANDALONE_SOLVER
436 if (solver_show_working) {
437 printf("%*s placing %s at (%d,%d)\n",
438 solver_recurse_depth*4, "",
439 names[j-1], j, i);
440 }
441#endif
442 latin_solver_place(solver, j-1, i-1, j);
443 }
444 }
445 return 1;
446 }
447 }
448
449 return 0;
450}
451
452static int solver_hard(struct latin_solver *solver, void *vctx)
453{
454 bool done_something = false;
455 int w = solver->o;
456#ifdef STANDALONE_SOLVER
457 char **names = solver->names;
458#endif
459 int i, j;
460
461 /*
462 * In identity-hidden mode, systematically rule out possibilities
463 * for the group identity.
464 *
465 * In solver_normal, we used the fact that any filled square in
466 * the grid whose contents _does_ match one of the elements it's
467 * the product of - that is, ab=a or ab=b - tells you immediately
468 * that the other element is the identity.
469 *
470 * Here, we use the flip side of that: any filled square in the
471 * grid whose contents does _not_ match either its row or column -
472 * that is, if ab is neither a nor b - tells you immediately that
473 * _neither_ of those elements is the identity. And if that's
474 * true, then we can also immediately rule out the possibility
475 * that it acts as the identity on any element at all.
476 */
477 for (i = 0; i < w; i++) {
478 bool i_can_be_id = true;
479#ifdef STANDALONE_SOLVER
480 char title[80];
481#endif
482
483 for (j = 0; j < w; j++) {
484 if (grid(i,j) && grid(i,j) != j+1) {
485#ifdef STANDALONE_SOLVER
486 if (solver_show_working)
487 sprintf(title, "%s cannot be the identity: "
488 "%s%s = %s =/= %s", names[i], names[i], names[j],
489 names[grid(i,j)-1], names[j]);
490#endif
491 i_can_be_id = false;
492 break;
493 }
494 if (grid(j,i) && grid(j,i) != j+1) {
495#ifdef STANDALONE_SOLVER
496 if (solver_show_working)
497 sprintf(title, "%s cannot be the identity: "
498 "%s%s = %s =/= %s", names[i], names[j], names[i],
499 names[grid(j,i)-1], names[j]);
500#endif
501 i_can_be_id = false;
502 break;
503 }
504 }
505
506 if (!i_can_be_id) {
507 /* Now rule out ij=j or ji=j for all j. */
508 for (j = 0; j < w; j++) {
509 if (cube(i, j, j+1)) {
510#ifdef STANDALONE_SOLVER
511 if (solver_show_working) {
512 if (title[0]) {
513 printf("%*s%s\n", solver_recurse_depth*4, "",
514 title);
515 title[0] = '\0';
516 }
517 printf("%*s ruling out %s at (%d,%d)\n",
518 solver_recurse_depth*4, "", names[j], i, j);
519 }
520#endif
521 cube(i, j, j+1) = false;
522 }
523 if (cube(j, i, j+1)) {
524#ifdef STANDALONE_SOLVER
525 if (solver_show_working) {
526 if (title[0]) {
527 printf("%*s%s\n", solver_recurse_depth*4, "",
528 title);
529 title[0] = '\0';
530 }
531 printf("%*s ruling out %s at (%d,%d)\n",
532 solver_recurse_depth*4, "", names[j], j, i);
533 }
534#endif
535 cube(j, i, j+1) = false;
536 }
537 }
538 }
539 }
540
541 return done_something;
542}
543
544#define SOLVER(upper,title,func,lower) func,
545static usersolver_t const group_solvers[] = { DIFFLIST(SOLVER) };
546
547static bool group_valid(struct latin_solver *solver, void *ctx)
548{
549 int w = solver->o;
550#ifdef STANDALONE_SOLVER
551 char **names = solver->names;
552#endif
553 int i, j, k;
554
555 for (i = 0; i < w; i++)
556 for (j = 0; j < w; j++)
557 for (k = 0; k < w; k++) {
558 int ij = grid(i, j) - 1;
559 int jk = grid(j, k) - 1;
560 int ij_k = grid(ij, k) - 1;
561 int i_jk = grid(i, jk) - 1;
562 if (ij_k != i_jk) {
563#ifdef STANDALONE_SOLVER
564 if (solver_show_working) {
565 printf("%*sfailure of associativity: "
566 "(%s%s)%s = %s%s = %s but "
567 "%s(%s%s) = %s%s = %s\n",
568 solver_recurse_depth*4, "",
569 names[i], names[j], names[k],
570 names[ij], names[k], names[ij_k],
571 names[i], names[j], names[k],
572 names[i], names[jk], names[i_jk]);
573 }
574#endif
575 return false;
576 }
577 }
578
579 return true;
580}
581
582static int solver(const game_params *params, digit *grid, int maxdiff)
583{
584 int w = params->w;
585 int ret;
586 struct latin_solver solver;
587
588#ifdef STANDALONE_SOLVER
589 char *p, text[100], *names[50];
590 int i;
591
592 for (i = 0, p = text; i < w; i++) {
593 names[i] = p;
594 *p++ = TOCHAR(i+1, params->id);
595 *p++ = '\0';
596 }
597 solver.names = names;
598#endif
599
600 if (latin_solver_alloc(&solver, grid, w))
601 ret = latin_solver_main(&solver, maxdiff,
602 DIFF_TRIVIAL, DIFF_HARD, DIFF_EXTREME,
603 DIFF_EXTREME, DIFF_UNREASONABLE,
604 group_solvers, group_valid, NULL, NULL, NULL);
605 else
606 ret = diff_impossible;
607
608 latin_solver_free(&solver);
609
610 return ret;
611}
612
613/* ----------------------------------------------------------------------
614 * Grid generation.
615 */
616
617static char *encode_grid(char *desc, digit *grid, int area)
618{
619 int run, i;
620 char *p = desc;
621
622 run = 0;
623 for (i = 0; i <= area; i++) {
624 int n = (i < area ? grid[i] : -1);
625
626 if (!n)
627 run++;
628 else {
629 if (run) {
630 while (run > 0) {
631 int c = 'a' - 1 + run;
632 if (run > 26)
633 c = 'z';
634 *p++ = c;
635 run -= c - ('a' - 1);
636 }
637 } else {
638 /*
639 * If there's a number in the very top left or
640 * bottom right, there's no point putting an
641 * unnecessary _ before or after it.
642 */
643 if (p > desc && n > 0)
644 *p++ = '_';
645 }
646 if (n > 0)
647 p += sprintf(p, "%d", n);
648 run = 0;
649 }
650 }
651 return p;
652}
653
654/* ----- data generated by group.gap begins ----- */
655
656struct group {
657 unsigned long autosize;
658 int order, ngens;
659 const char *gens;
660};
661struct groups {
662 int ngroups;
663 const struct group *groups;
664};
665
666static const struct group groupdata[] = {
667 /* order 2 */
668 {1L, 2, 1, "BA"},
669 /* order 3 */
670 {2L, 3, 1, "BCA"},
671 /* order 4 */
672 {2L, 4, 1, "BCDA"},
673 {6L, 4, 2, "BADC" "CDAB"},
674 /* order 5 */
675 {4L, 5, 1, "BCDEA"},
676 /* order 6 */
677 {6L, 6, 2, "CFEBAD" "BADCFE"},
678 {2L, 6, 1, "DCFEBA"},
679 /* order 7 */
680 {6L, 7, 1, "BCDEFGA"},
681 /* order 8 */
682 {4L, 8, 1, "BCEFDGHA"},
683 {8L, 8, 2, "BDEFGAHC" "EGBHDCFA"},
684 {8L, 8, 2, "EGBHDCFA" "BAEFCDHG"},
685 {24L, 8, 2, "BDEFGAHC" "CHDGBEAF"},
686 {168L, 8, 3, "BAEFCDHG" "CEAGBHDF" "DFGAHBCE"},
687 /* order 9 */
688 {6L, 9, 1, "BDECGHFIA"},
689 {48L, 9, 2, "BDEAGHCIF" "CEFGHAIBD"},
690 /* order 10 */
691 {20L, 10, 2, "CJEBGDIFAH" "BADCFEHGJI"},
692 {4L, 10, 1, "DCFEHGJIBA"},
693 /* order 11 */
694 {10L, 11, 1, "BCDEFGHIJKA"},
695 /* order 12 */
696 {12L, 12, 2, "GLDKJEHCBIAF" "BCEFAGIJDKLH"},
697 {4L, 12, 1, "EHIJKCBLDGFA"},
698 {24L, 12, 2, "BEFGAIJKCDLH" "FJBKHLEGDCIA"},
699 {12L, 12, 2, "GLDKJEHCBIAF" "BAEFCDIJGHLK"},
700 {12L, 12, 2, "FDIJGHLBKAEC" "GIDKFLHCJEAB"},
701 /* order 13 */
702 {12L, 13, 1, "BCDEFGHIJKLMA"},
703 /* order 14 */
704 {42L, 14, 2, "ELGNIBKDMFAHCJ" "BADCFEHGJILKNM"},
705 {6L, 14, 1, "FEHGJILKNMBADC"},
706 /* order 15 */
707 {8L, 15, 1, "EGHCJKFMNIOBLDA"},
708 /* order 16 */
709 {8L, 16, 1, "MKNPFOADBGLCIEHJ"},
710 {96L, 16, 2, "ILKCONFPEDJHGMAB" "BDFGHIAKLMNCOEPJ"},
711 {32L, 16, 2, "MIHPFDCONBLAKJGE" "BEFGHJKALMNOCDPI"},
712 {32L, 16, 2, "IFACOGLMDEJBNPKH" "BEFGHJKALMNOCDPI"},
713 {16L, 16, 2, "MOHPFKCINBLADJGE" "BDFGHIEKLMNJOAPC"},
714 {16L, 16, 2, "MIHPFDJONBLEKCGA" "BDFGHIEKLMNJOAPC"},
715 {32L, 16, 2, "MOHPFDCINBLEKJGA" "BAFGHCDELMNIJKPO"},
716 {16L, 16, 2, "MIHPFKJONBLADCGE" "GDPHNOEKFLBCIAMJ"},
717 {32L, 16, 2, "MIBPFDJOGHLEKCNA" "CLEIJGMPKAOHNFDB"},
718 {192L, 16, 3,
719 "MCHPFAIJNBLDEOGK" "BEFGHJKALMNOCDPI" "GKLBNOEDFPHJIAMC"},
720 {64L, 16, 3, "MCHPFAIJNBLDEOGK" "LOGFPKJIBNMEDCHA" "CMAIJHPFDEONBLKG"},
721 {192L, 16, 3,
722 "IPKCOGMLEDJBNFAH" "BEFGHJKALMNOCDPI" "CMEIJBPFKAOGHLDN"},
723 {48L, 16, 3, "IPDJONFLEKCBGMAH" "FJBLMEOCGHPKAIND" "DGIEKLHNJOAMPBCF"},
724 {20160L, 16, 4,
725 "EHJKAMNBOCDPFGIL" "BAFGHCDELMNIJKPO" "CFAIJBLMDEOGHPKN"
726 "DGIAKLBNCOEFPHJM"},
727 /* order 17 */
728 {16L, 17, 1, "EFGHIJKLMNOPQABCD"},
729 /* order 18 */
730 {54L, 18, 2, "MKIQOPNAGLRECDBJHF" "BAEFCDJKLGHIOPMNRQ"},
731 {6L, 18, 1, "ECJKGHFOPDMNLRIQBA"},
732 {12L, 18, 2, "ECJKGHBOPAMNFRDQLI" "KNOPQCFREIGHLJAMBD"},
733 {432L, 18, 3,
734 "IFNAKLQCDOPBGHREMJ" "NOQCFRIGHKLJAMPBDE" "BAEFCDJKLGHIOPMNRQ"},
735 {48L, 18, 2, "ECJKGHBOPAMNFRDQLI" "FDKLHIOPBMNAREQCJG"},
736 /* order 19 */
737 {18L, 19, 1, "EFGHIJKLMNOPQRSABCD"},
738 /* order 20 */
739 {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "EABICDFMGHJQKLNTOPRS"},
740 {8L, 20, 1, "EHIJLCMNPGQRSKBTDOFA"},
741 {20L, 20, 2, "DJSHQNCLTRGPEBKAIFOM" "EABICDFMGHJQKLNTOPRS"},
742 {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "ECBIAGFMDKJQHONTLSRP"},
743 {24L, 20, 2, "IGFMDKJQHONTLSREPCBA" "FDIJGHMNKLQROPTBSAEC"},
744 /* order 21 */
745 {42L, 21, 2, "ITLSBOUERDHAGKCJNFMQP" "EJHLMKOPNRSQAUTCDBFGI"},
746 {12L, 21, 1, "EGHCJKFMNIPQLSTOUBRDA"},
747 /* order 22 */
748 {110L, 22, 2, "ETGVIBKDMFOHQJSLUNAPCR" "BADCFEHGJILKNMPORQTSVU"},
749 {10L, 22, 1, "FEHGJILKNMPORQTSVUBADC"},
750 /* order 23 */
751 {22L, 23, 1, "EFGHIJKLMNOPQRSTUVWABCD"},
752 /* order 24 */
753 {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "HRNOPSWCTUVBLDIJXFGAKQME"},
754 {8L, 24, 1, "MQBTUDRWFGHXJELINOPKSAVC"},
755 {24L, 24, 2, "IOQRBEUVFWGHKLAXMNPSCDTJ" "NJXOVGDKSMTFIPQELCURBWAH"},
756 {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "HSNOPWLDTUVBRIAKXFGCQEMJ"},
757 {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "TWHNXLRIOPUMSACQVBFDEJGK"},
758 {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "BAFGHCDEMNOPIJKLTUVQRSXW"},
759 {48L, 24, 3,
760 "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO"
761 "HSNOPWLDTUVBRIAKXFGCQEMJ"},
762 {24L, 24, 3,
763 "QUKJWPXFESRIVBMNLDCGHTAO" "JXEQRVUMKLWCPGFTSAIBONDH"
764 "TRONXLWCHVUMSAIJPGFDEQBK"},
765 {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "VKXHOQASNTPBCWDEUFGIJLMR"},
766 {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "RMLWIGTUSDJQOPFXEKCBVNAH"},
767 {48L, 24, 2, "IULQRGXMSDCWOPNTEKJBVFAH" "GLMOPRSDTUBVWIEKFXHJQANC"},
768 {24L, 24, 2, "UJPXMRCSNHGTLWIKFVBEDQOA" "NRUFVLWIPXMOJEDQHGTCSABK"},
769 {24L, 24, 2, "MIBTUAQRFGHXCDEWNOPJKLVS" "OKXVFWSCGUTNDRQJBPMALIHE"},
770 {144L, 24, 3,
771 "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO"
772 "BAFGHCDEMNOPIJKLTUVQRSXW"},
773 {336L, 24, 3,
774 "QTKJWONXESRIHVUMLDCPGFAB" "JNEQRHTUKLWCOPXFSAIVBMDG"
775 "HENOPJKLTUVBQRSAXFGWCDMI"},
776 /* order 25 */
777 {20L, 25, 1, "EHILMNPQRSFTUVBJWXDOYGAKC"},
778 {480L, 25, 2, "EHILMNPQRSCTUVBFWXDJYGOKA" "BDEGHIKLMNAPQRSCTUVFWXJYO"},
779 /* order 26 */
780 {156L, 26, 2,
781 "EXGZIBKDMFOHQJSLUNWPYRATCV" "BADCFEHGJILKNMPORQTSVUXWZY"},
782 {12L, 26, 1, "FEHGJILKNMPORQTSVUXWZYBADC"},
783};
784
785static const struct groups groups[] = {
786 {0, NULL}, /* trivial case: 0 */
787 {0, NULL}, /* trivial case: 1 */
788 {1, groupdata + 0}, /* 2 */
789 {1, groupdata + 1}, /* 3 */
790 {2, groupdata + 2}, /* 4 */
791 {1, groupdata + 4}, /* 5 */
792 {2, groupdata + 5}, /* 6 */
793 {1, groupdata + 7}, /* 7 */
794 {5, groupdata + 8}, /* 8 */
795 {2, groupdata + 13}, /* 9 */
796 {2, groupdata + 15}, /* 10 */
797 {1, groupdata + 17}, /* 11 */
798 {5, groupdata + 18}, /* 12 */
799 {1, groupdata + 23}, /* 13 */
800 {2, groupdata + 24}, /* 14 */
801 {1, groupdata + 26}, /* 15 */
802 {14, groupdata + 27}, /* 16 */
803 {1, groupdata + 41}, /* 17 */
804 {5, groupdata + 42}, /* 18 */
805 {1, groupdata + 47}, /* 19 */
806 {5, groupdata + 48}, /* 20 */
807 {2, groupdata + 53}, /* 21 */
808 {2, groupdata + 55}, /* 22 */
809 {1, groupdata + 57}, /* 23 */
810 {15, groupdata + 58}, /* 24 */
811 {2, groupdata + 73}, /* 25 */
812 {2, groupdata + 75}, /* 26 */
813};
814
815/* ----- data generated by group.gap ends ----- */
816
817static char *new_game_desc(const game_params *params, random_state *rs,
818 char **aux, bool interactive)
819{
820 int w = params->w, a = w*w;
821 digit *grid, *soln, *soln2;
822 int *indices;
823 int i, j, k, qh, qt;
824 int diff = params->diff;
825 const struct group *group;
826 char *desc, *p;
827
828 /*
829 * Difficulty exceptions: some combinations of size and
830 * difficulty cannot be satisfied, because all puzzles of at
831 * most that difficulty are actually even easier.
832 *
833 * Remember to re-test this whenever a change is made to the
834 * solver logic!
835 *
836 * I tested it using the following shell command:
837
838for d in t n h x u; do
839 for id in '' i; do
840 for i in {3..9}; do
841 echo -n "./group --generate 1 ${i}d${d}${id}: "
842 perl -e 'alarm 30; exec @ARGV' \
843 ./group --generate 1 ${i}d${d}${id} >/dev/null && echo ok
844 done
845 done
846done
847
848 * Of course, it's better to do that after taking the exceptions
849 * _out_, so as to detect exceptions that should be removed as
850 * well as those which should be added.
851 */
852 if (w < 5 && diff == DIFF_UNREASONABLE)
853 diff--;
854 if ((w < 5 || ((w == 6 || w == 8) && params->id)) && diff == DIFF_EXTREME)
855 diff--;
856 if ((w < 6 || (w == 6 && params->id)) && diff == DIFF_HARD)
857 diff--;
858 if ((w < 4 || (w == 4 && params->id)) && diff == DIFF_NORMAL)
859 diff--;
860
861 grid = snewn(a, digit);
862 soln = snewn(a, digit);
863 soln2 = snewn(a, digit);
864 indices = snewn(a, int);
865
866 while (1) {
867 /*
868 * Construct a valid group table, by picking a group from
869 * the above data table, decompressing it into a full
870 * representation by BFS, and then randomly permuting its
871 * non-identity elements.
872 *
873 * We build the canonical table in 'soln' (and use 'grid' as
874 * our BFS queue), then transfer the table into 'grid'
875 * having shuffled the rows.
876 */
877 assert(w >= 2);
878 assert(w < lenof(groups));
879 group = groups[w].groups + random_upto(rs, groups[w].ngroups);
880 assert(group->order == w);
881 memset(soln, 0, a);
882 for (i = 0; i < w; i++)
883 soln[i] = i+1;
884 qh = qt = 0;
885 grid[qt++] = 1;
886 while (qh < qt) {
887 digit *row, *newrow;
888
889 i = grid[qh++];
890 row = soln + (i-1)*w;
891
892 for (j = 0; j < group->ngens; j++) {
893 int nri;
894 const char *gen = group->gens + j*w;
895
896 /*
897 * Apply each group generator to row, constructing a
898 * new row.
899 */
900 nri = gen[row[0]-1] - 'A' + 1; /* which row is it? */
901 newrow = soln + (nri-1)*w;
902 if (!newrow[0]) { /* not done yet */
903 for (k = 0; k < w; k++)
904 newrow[k] = gen[row[k]-1] - 'A' + 1;
905 grid[qt++] = nri;
906 }
907 }
908 }
909 /* That's got the canonical table. Now shuffle it. */
910 for (i = 0; i < w; i++)
911 soln2[i] = i;
912 if (params->id) /* do we shuffle in the identity? */
913 shuffle(soln2+1, w-1, sizeof(*soln2), rs);
914 else
915 shuffle(soln2, w, sizeof(*soln2), rs);
916 for (i = 0; i < w; i++)
917 for (j = 0; j < w; j++)
918 grid[(soln2[i])*w+(soln2[j])] = soln2[soln[i*w+j]-1]+1;
919
920 /*
921 * Remove entries one by one while the puzzle is still
922 * soluble at the appropriate difficulty level.
923 */
924 memcpy(soln, grid, a);
925 if (!params->id) {
926 /*
927 * Start by blanking the entire identity row and column,
928 * and also another row and column so that the player
929 * can't trivially determine which element is the
930 * identity.
931 */
932
933 j = 1 + random_upto(rs, w-1); /* pick a second row/col to blank */
934 for (i = 0; i < w; i++) {
935 grid[(soln2[0])*w+i] = grid[i*w+(soln2[0])] = 0;
936 grid[(soln2[j])*w+i] = grid[i*w+(soln2[j])] = 0;
937 }
938
939 memcpy(soln2, grid, a);
940 if (solver(params, soln2, diff) > diff)
941 continue; /* go round again if that didn't work */
942 }
943
944 k = 0;
945 for (i = (params->id ? 1 : 0); i < w; i++)
946 for (j = (params->id ? 1 : 0); j < w; j++)
947 if (grid[i*w+j])
948 indices[k++] = i*w+j;
949 shuffle(indices, k, sizeof(*indices), rs);
950
951 for (i = 0; i < k; i++) {
952 memcpy(soln2, grid, a);
953 soln2[indices[i]] = 0;
954 if (solver(params, soln2, diff) <= diff)
955 grid[indices[i]] = 0;
956 }
957
958 /*
959 * Make sure the puzzle isn't too easy.
960 */
961 if (diff > 0) {
962 memcpy(soln2, grid, a);
963 if (solver(params, soln2, diff-1) < diff)
964 continue; /* go round and try again */
965 }
966
967 /*
968 * Done.
969 */
970 break;
971 }
972
973 /*
974 * Encode the puzzle description.
975 */
976 desc = snewn(a*20, char);
977 p = encode_grid(desc, grid, a);
978 *p++ = '\0';
979 desc = sresize(desc, p - desc, char);
980
981 /*
982 * Encode the solution.
983 */
984 *aux = snewn(a+2, char);
985 (*aux)[0] = 'S';
986 for (i = 0; i < a; i++)
987 (*aux)[i+1] = TOCHAR(soln[i], params->id);
988 (*aux)[a+1] = '\0';
989
990 sfree(grid);
991 sfree(soln);
992 sfree(soln2);
993 sfree(indices);
994
995 return desc;
996}
997
998/* ----------------------------------------------------------------------
999 * Gameplay.
1000 */
1001
1002static const char *validate_grid_desc(const char **pdesc, int range, int area)
1003{
1004 const char *desc = *pdesc;
1005 int squares = 0;
1006 while (*desc && *desc != ',') {
1007 int n = *desc++;
1008 if (n >= 'a' && n <= 'z') {
1009 squares += n - 'a' + 1;
1010 } else if (n == '_') {
1011 /* do nothing */;
1012 } else if (n > '0' && n <= '9') {
1013 int val = atoi(desc-1);
1014 if (val < 1 || val > range)
1015 return "Out-of-range number in game description";
1016 squares++;
1017 while (*desc >= '0' && *desc <= '9')
1018 desc++;
1019 } else
1020 return "Invalid character in game description";
1021 }
1022
1023 if (squares < area)
1024 return "Not enough data to fill grid";
1025
1026 if (squares > area)
1027 return "Too much data to fit in grid";
1028 *pdesc = desc;
1029 return NULL;
1030}
1031
1032static const char *validate_desc(const game_params *params, const char *desc)
1033{
1034 int w = params->w, a = w*w;
1035 const char *p = desc;
1036
1037 return validate_grid_desc(&p, w, a);
1038}
1039
1040static const char *spec_to_grid(const char *desc, digit *grid, int area)
1041{
1042 int i = 0;
1043 while (*desc && *desc != ',') {
1044 int n = *desc++;
1045 if (n >= 'a' && n <= 'z') {
1046 int run = n - 'a' + 1;
1047 assert(i + run <= area);
1048 while (run-- > 0)
1049 grid[i++] = 0;
1050 } else if (n == '_') {
1051 /* do nothing */;
1052 } else if (n > '0' && n <= '9') {
1053 assert(i < area);
1054 grid[i++] = atoi(desc-1);
1055 while (*desc >= '0' && *desc <= '9')
1056 desc++;
1057 } else {
1058 assert(!"We can't get here");
1059 }
1060 }
1061 assert(i == area);
1062 return desc;
1063}
1064
1065static game_state *new_game(midend *me, const game_params *params,
1066 const char *desc)
1067{
1068 int w = params->w, a = w*w;
1069 game_state *state = snew(game_state);
1070 int i;
1071
1072 state->par = *params; /* structure copy */
1073 state->grid = snewn(a, digit);
1074 state->common = snew(group_common);
1075 state->common->refcount = 1;
1076 state->common->immutable = snewn(a, bool);
1077 state->pencil = snewn(a, int);
1078 for (i = 0; i < a; i++) {
1079 state->grid[i] = 0;
1080 state->common->immutable[i] = false;
1081 state->pencil[i] = 0;
1082 }
1083 state->sequence = snewn(w, digit);
1084 state->dividers = snewn(w, int);
1085 for (i = 0; i < w; i++) {
1086 state->sequence[i] = i;
1087 state->dividers[i] = -1;
1088 }
1089
1090 desc = spec_to_grid(desc, state->grid, a);
1091 for (i = 0; i < a; i++)
1092 if (state->grid[i] != 0)
1093 state->common->immutable[i] = true;
1094
1095 state->completed = false;
1096 state->cheated = false;
1097
1098 return state;
1099}
1100
1101static game_state *dup_game(const game_state *state)
1102{
1103 int w = state->par.w, a = w*w;
1104 game_state *ret = snew(game_state);
1105
1106 ret->par = state->par; /* structure copy */
1107
1108 ret->grid = snewn(a, digit);
1109 ret->common = state->common;
1110 ret->common->refcount++;
1111 ret->pencil = snewn(a, int);
1112 ret->sequence = snewn(w, digit);
1113 ret->dividers = snewn(w, int);
1114 memcpy(ret->grid, state->grid, a*sizeof(digit));
1115 memcpy(ret->pencil, state->pencil, a*sizeof(int));
1116 memcpy(ret->sequence, state->sequence, w*sizeof(digit));
1117 memcpy(ret->dividers, state->dividers, w*sizeof(int));
1118
1119 ret->completed = state->completed;
1120 ret->cheated = state->cheated;
1121
1122 return ret;
1123}
1124
1125static void free_game(game_state *state)
1126{
1127 sfree(state->grid);
1128 if (--state->common->refcount == 0) {
1129 sfree(state->common->immutable);
1130 sfree(state->common);
1131 }
1132 sfree(state->pencil);
1133 sfree(state->sequence);
1134 sfree(state);
1135}
1136
1137static char *solve_game(const game_state *state, const game_state *currstate,
1138 const char *aux, const char **error)
1139{
1140 int w = state->par.w, a = w*w;
1141 int i, ret;
1142 digit *soln;
1143 char *out;
1144
1145 if (aux)
1146 return dupstr(aux);
1147
1148 soln = snewn(a, digit);
1149 memcpy(soln, state->grid, a*sizeof(digit));
1150
1151 ret = solver(&state->par, soln, DIFFCOUNT-1);
1152
1153 if (ret == diff_impossible) {
1154 *error = "No solution exists for this puzzle";
1155 out = NULL;
1156 } else if (ret == diff_ambiguous) {
1157 *error = "Multiple solutions exist for this puzzle";
1158 out = NULL;
1159 } else {
1160 out = snewn(a+2, char);
1161 out[0] = 'S';
1162 for (i = 0; i < a; i++)
1163 out[i+1] = TOCHAR(soln[i], state->par.id);
1164 out[a+1] = '\0';
1165 }
1166
1167 sfree(soln);
1168 return out;
1169}
1170
1171static bool game_can_format_as_text_now(const game_params *params)
1172{
1173 return true;
1174}
1175
1176static char *game_text_format(const game_state *state)
1177{
1178 int w = state->par.w;
1179 int x, y;
1180 char *ret, *p, ch;
1181
1182 ret = snewn(2*w*w+1, char); /* leave room for terminating NUL */
1183
1184 p = ret;
1185 for (y = 0; y < w; y++) {
1186 for (x = 0; x < w; x++) {
1187 digit d = state->grid[y*w+x];
1188
1189 if (d == 0) {
1190 ch = '.';
1191 } else {
1192 ch = TOCHAR(d, state->par.id);
1193 }
1194
1195 *p++ = ch;
1196 if (x == w-1) {
1197 *p++ = '\n';
1198 } else {
1199 *p++ = ' ';
1200 }
1201 }
1202 }
1203
1204 assert(p - ret == 2*w*w);
1205 *p = '\0';
1206 return ret;
1207}
1208
1209struct game_ui {
1210 /*
1211 * These are the coordinates of the primary highlighted square on
1212 * the grid, if hshow = 1.
1213 */
1214 int hx, hy;
1215 /*
1216 * These are the coordinates hx,hy _before_ they go through
1217 * state->sequence.
1218 */
1219 int ohx, ohy;
1220 /*
1221 * These variables give the length and displacement of a diagonal
1222 * sequence of highlighted squares starting at ohx,ohy (still if
1223 * hshow = 1). To find the squares' real coordinates, for 0<=i<dn,
1224 * compute ohx+i*odx and ohy+i*ody and then map through
1225 * state->sequence.
1226 */
1227 int odx, ody, odn;
1228 /*
1229 * This indicates whether the current highlight is a
1230 * pencil-mark one or a real one.
1231 */
1232 bool hpencil;
1233 /*
1234 * This indicates whether or not we're showing the highlight
1235 * (used to be hx = hy = -1); important so that when we're
1236 * using the cursor keys it doesn't keep coming back at a
1237 * fixed position. When hshow = 1, pressing a valid number
1238 * or letter key or Space will enter that number or letter in the grid.
1239 */
1240 bool hshow;
1241 /*
1242 * This indicates whether we're using the highlight as a cursor;
1243 * it means that it doesn't vanish on a keypress, and that it is
1244 * allowed on immutable squares.
1245 */
1246 bool hcursor;
1247 /*
1248 * This indicates whether we're dragging a table header to
1249 * reposition an entire row or column.
1250 */
1251 int drag; /* 0=none 1=row 2=col */
1252 int dragnum; /* element being dragged */
1253 int dragpos; /* its current position */
1254 int edgepos;
1255
1256 /*
1257 * User preference option: if the user right-clicks in a square
1258 * and presses a letter key to add/remove a pencil mark, do we
1259 * hide the mouse highlight again afterwards?
1260 *
1261 * Historically our answer was yes. The Android port prefers no.
1262 * There are advantages both ways, depending how much you dislike
1263 * the highlight cluttering your view. So it's a preference.
1264 */
1265 bool pencil_keep_highlight;
1266};
1267
1268static game_ui *new_ui(const game_state *state)
1269{
1270 game_ui *ui = snew(game_ui);
1271
1272 ui->hx = ui->hy = 0;
1273 ui->hpencil = false;
1274 ui->hshow = false;
1275 ui->hcursor = false;
1276 ui->drag = 0;
1277
1278 ui->pencil_keep_highlight = false;
1279
1280 return ui;
1281}
1282
1283static void free_ui(game_ui *ui)
1284{
1285 sfree(ui);
1286}
1287
1288static config_item *get_prefs(game_ui *ui)
1289{
1290 config_item *ret;
1291
1292 ret = snewn(2, config_item);
1293
1294 ret[0].name = "Keep mouse highlight after changing a pencil mark";
1295 ret[0].kw = "pencil-keep-highlight";
1296 ret[0].type = C_BOOLEAN;
1297 ret[0].u.boolean.bval = ui->pencil_keep_highlight;
1298
1299 ret[1].name = NULL;
1300 ret[1].type = C_END;
1301
1302 return ret;
1303}
1304
1305static void set_prefs(game_ui *ui, const config_item *cfg)
1306{
1307 ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
1308}
1309
1310static void game_changed_state(game_ui *ui, const game_state *oldstate,
1311 const game_state *newstate)
1312{
1313 int w = newstate->par.w;
1314 /*
1315 * We prevent pencil-mode highlighting of a filled square, unless
1316 * we're using the cursor keys. So if the user has just filled in
1317 * a square which we had a pencil-mode highlight in (by Undo, or
1318 * by Redo, or by Solve), then we cancel the highlight.
1319 */
1320 if (ui->hshow && ui->hpencil && !ui->hcursor &&
1321 newstate->grid[ui->hy * w + ui->hx] != 0) {
1322 ui->hshow = false;
1323 }
1324 if (ui->hshow && ui->odn > 1) {
1325 /*
1326 * Reordering of rows or columns within the range of a
1327 * multifill selection cancels the multifill and deselects
1328 * everything.
1329 */
1330 int i;
1331 for (i = 0; i < ui->odn; i++) {
1332 if (oldstate->sequence[ui->ohx + i*ui->odx] !=
1333 newstate->sequence[ui->ohx + i*ui->odx]) {
1334 ui->hshow = false;
1335 break;
1336 }
1337 if (oldstate->sequence[ui->ohy + i*ui->ody] !=
1338 newstate->sequence[ui->ohy + i*ui->ody]) {
1339 ui->hshow = false;
1340 break;
1341 }
1342 }
1343 } else if (ui->hshow &&
1344 (newstate->sequence[ui->ohx] != ui->hx ||
1345 newstate->sequence[ui->ohy] != ui->hy)) {
1346 /*
1347 * Otherwise, reordering of the row or column containing the
1348 * selection causes the selection to move with it.
1349 */
1350 int i;
1351 for (i = 0; i < w; i++) {
1352 if (newstate->sequence[i] == ui->hx)
1353 ui->ohx = i;
1354 if (newstate->sequence[i] == ui->hy)
1355 ui->ohy = i;
1356 }
1357 }
1358}
1359
1360static const char *current_key_label(const game_ui *ui,
1361 const game_state *state, int button)
1362{
1363 if (ui->hshow && button == CURSOR_SELECT)
1364 return ui->hpencil ? "Ink" : "Pencil";
1365 if (ui->hshow && button == CURSOR_SELECT2) {
1366 int w = state->par.w;
1367 int i;
1368 for (i = 0; i < ui->odn; i++) {
1369 int x = state->sequence[ui->ohx + i*ui->odx];
1370 int y = state->sequence[ui->ohy + i*ui->ody];
1371 int index = y*w+x;
1372 if (ui->hpencil && state->grid[index]) return "";
1373 if (state->common->immutable[index]) return "";
1374 }
1375 return "Clear";
1376 }
1377 return "";
1378}
1379
1380#define PREFERRED_TILESIZE 48
1381#define TILESIZE (ds->tilesize)
1382#define BORDER (TILESIZE / 2)
1383#define LEGEND (TILESIZE)
1384#define GRIDEXTRA max((TILESIZE / 32),1)
1385#define COORD(x) ((x)*TILESIZE + BORDER + LEGEND)
1386#define FROMCOORD(x) (((x)+(TILESIZE-BORDER-LEGEND)) / TILESIZE - 1)
1387
1388#define FLASH_TIME 0.4F
1389
1390#define DF_DIVIDER_TOP 0x1000
1391#define DF_DIVIDER_BOT 0x2000
1392#define DF_DIVIDER_LEFT 0x4000
1393#define DF_DIVIDER_RIGHT 0x8000
1394#define DF_HIGHLIGHT 0x0400
1395#define DF_HIGHLIGHT_PENCIL 0x0200
1396#define DF_IMMUTABLE 0x0100
1397#define DF_LEGEND 0x0080
1398#define DF_DIGIT_MASK 0x001F
1399
1400#define EF_DIGIT_SHIFT 5
1401#define EF_DIGIT_MASK ((1 << EF_DIGIT_SHIFT) - 1)
1402#define EF_LEFT_SHIFT 0
1403#define EF_RIGHT_SHIFT (3*EF_DIGIT_SHIFT)
1404#define EF_LEFT_MASK ((1UL << (3*EF_DIGIT_SHIFT)) - 1UL)
1405#define EF_RIGHT_MASK (EF_LEFT_MASK << EF_RIGHT_SHIFT)
1406#define EF_LATIN (1UL << (6*EF_DIGIT_SHIFT))
1407
1408struct game_drawstate {
1409 game_params par;
1410 int w, tilesize;
1411 bool started;
1412 long *tiles, *legend, *pencil, *errors;
1413 long *errtmp;
1414 digit *sequence;
1415};
1416
1417static bool check_errors(const game_state *state, long *errors)
1418{
1419 int w = state->par.w, a = w*w;
1420 digit *grid = state->grid;
1421 int i, j, k, x, y;
1422 bool errs = false;
1423
1424 /*
1425 * To verify that we have a valid group table, it suffices to
1426 * test latin-square-hood and associativity only. All the other
1427 * group axioms follow from those two.
1428 *
1429 * Proof:
1430 *
1431 * Associativity is given; closure is obvious from latin-
1432 * square-hood. We need to show that an identity exists and that
1433 * every element has an inverse.
1434 *
1435 * Identity: take any element a. There will be some element e
1436 * such that ea=a (in a latin square, every element occurs in
1437 * every row and column, so a must occur somewhere in the a
1438 * column, say on row e). For any other element b, there must
1439 * exist x such that ax=b (same argument from latin-square-hood
1440 * again), and then associativity gives us eb = e(ax) = (ea)x =
1441 * ax = b. Hence eb=b for all b, i.e. e is a left-identity. A
1442 * similar argument tells us that there must be some f which is
1443 * a right-identity, and then we show they are the same element
1444 * by observing that ef must simultaneously equal e and equal f.
1445 *
1446 * Inverses: given any a, by the latin-square argument again,
1447 * there must exist p and q such that pa=e and aq=e (i.e. left-
1448 * and right-inverses). We can show these are equal by
1449 * associativity: p = pe = p(aq) = (pa)q = eq = q. []
1450 */
1451
1452 if (errors)
1453 for (i = 0; i < a; i++)
1454 errors[i] = 0;
1455
1456 for (y = 0; y < w; y++) {
1457 unsigned long mask = 0, errmask = 0;
1458 for (x = 0; x < w; x++) {
1459 unsigned long bit = 1UL << grid[y*w+x];
1460 errmask |= (mask & bit);
1461 mask |= bit;
1462 }
1463
1464 if (mask != (1 << (w+1)) - (1 << 1)) {
1465 errs = true;
1466 errmask &= ~1UL;
1467 if (errors) {
1468 for (x = 0; x < w; x++)
1469 if (errmask & (1UL << grid[y*w+x]))
1470 errors[y*w+x] |= EF_LATIN;
1471 }
1472 }
1473 }
1474
1475 for (x = 0; x < w; x++) {
1476 unsigned long mask = 0, errmask = 0;
1477 for (y = 0; y < w; y++) {
1478 unsigned long bit = 1UL << grid[y*w+x];
1479 errmask |= (mask & bit);
1480 mask |= bit;
1481 }
1482
1483 if (mask != (1 << (w+1)) - (1 << 1)) {
1484 errs = true;
1485 errmask &= ~1UL;
1486 if (errors) {
1487 for (y = 0; y < w; y++)
1488 if (errmask & (1UL << grid[y*w+x]))
1489 errors[y*w+x] |= EF_LATIN;
1490 }
1491 }
1492 }
1493
1494 for (i = 1; i < w; i++)
1495 for (j = 1; j < w; j++)
1496 for (k = 1; k < w; k++)
1497 if (grid[i*w+j] && grid[j*w+k] &&
1498 grid[(grid[i*w+j]-1)*w+k] &&
1499 grid[i*w+(grid[j*w+k]-1)] &&
1500 grid[(grid[i*w+j]-1)*w+k] != grid[i*w+(grid[j*w+k]-1)]) {
1501 if (errors) {
1502 int a = i+1, b = j+1, c = k+1;
1503 int ab = grid[i*w+j], bc = grid[j*w+k];
1504 int left = (ab-1)*w+(c-1), right = (a-1)*w+(bc-1);
1505 /*
1506 * If the appropriate error slot is already
1507 * used for one of the squares, we don't
1508 * fill either of them.
1509 */
1510 if (!(errors[left] & EF_LEFT_MASK) &&
1511 !(errors[right] & EF_RIGHT_MASK)) {
1512 long err;
1513 err = a;
1514 err = (err << EF_DIGIT_SHIFT) | b;
1515 err = (err << EF_DIGIT_SHIFT) | c;
1516 errors[left] |= err << EF_LEFT_SHIFT;
1517 errors[right] |= err << EF_RIGHT_SHIFT;
1518 }
1519 }
1520 errs = true;
1521 }
1522
1523 return errs;
1524}
1525
1526static int find_in_sequence(digit *seq, int len, digit n)
1527{
1528 int i;
1529
1530 for (i = 0; i < len; i++)
1531 if (seq[i] == n)
1532 return i;
1533
1534 assert(!"Should never get here");
1535 return -1;
1536}
1537
1538static char *interpret_move(const game_state *state, game_ui *ui,
1539 const game_drawstate *ds,
1540 int x, int y, int button)
1541{
1542 int w = state->par.w;
1543 int tx, ty;
1544 char buf[80];
1545
1546 button = STRIP_BUTTON_MODIFIERS(button);
1547
1548 tx = FROMCOORD(x);
1549 ty = FROMCOORD(y);
1550
1551 if (ui->drag) {
1552 if (IS_MOUSE_DRAG(button)) {
1553 int tcoord = ((ui->drag &~ 4) == 1 ? ty : tx);
1554 ui->drag |= 4; /* some movement has happened */
1555 if (tcoord >= 0 && tcoord < w) {
1556 ui->dragpos = tcoord;
1557 return MOVE_UI_UPDATE;
1558 }
1559 } else if (IS_MOUSE_RELEASE(button)) {
1560 if (ui->drag & 4) {
1561 ui->drag = 0; /* end drag */
1562 if (state->sequence[ui->dragpos] == ui->dragnum)
1563 return MOVE_UI_UPDATE; /* drag was a no-op overall */
1564 sprintf(buf, "D%d,%d", ui->dragnum, ui->dragpos);
1565 return dupstr(buf);
1566 } else {
1567 ui->drag = 0; /* end 'drag' */
1568 if (ui->edgepos > 0 && ui->edgepos < w) {
1569 sprintf(buf, "V%d,%d",
1570 state->sequence[ui->edgepos-1],
1571 state->sequence[ui->edgepos]);
1572 return dupstr(buf);
1573 } else
1574 return MOVE_UI_UPDATE; /* no-op */
1575 }
1576 }
1577 } else if (IS_MOUSE_DOWN(button)) {
1578 if (tx >= 0 && tx < w && ty >= 0 && ty < w) {
1579 int otx = tx, oty = ty;
1580 tx = state->sequence[tx];
1581 ty = state->sequence[ty];
1582 if (button == LEFT_BUTTON) {
1583 if (tx == ui->hx && ty == ui->hy &&
1584 ui->hshow && !ui->hpencil) {
1585 ui->hshow = false;
1586 } else {
1587 ui->hx = tx;
1588 ui->hy = ty;
1589 ui->ohx = otx;
1590 ui->ohy = oty;
1591 ui->odx = ui->ody = 0;
1592 ui->odn = 1;
1593 ui->hshow = !state->common->immutable[ty*w+tx];
1594 ui->hpencil = false;
1595 }
1596 ui->hcursor = false;
1597 return MOVE_UI_UPDATE;
1598 }
1599 if (button == RIGHT_BUTTON) {
1600 /*
1601 * Pencil-mode highlighting for non filled squares.
1602 */
1603 if (state->grid[ty*w+tx] == 0) {
1604 if (tx == ui->hx && ty == ui->hy &&
1605 ui->hshow && ui->hpencil) {
1606 ui->hshow = false;
1607 } else {
1608 ui->hpencil = true;
1609 ui->hx = tx;
1610 ui->hy = ty;
1611 ui->ohx = otx;
1612 ui->ohy = oty;
1613 ui->odx = ui->ody = 0;
1614 ui->odn = 1;
1615 ui->hshow = true;
1616 }
1617 } else {
1618 ui->hshow = false;
1619 }
1620 ui->hcursor = false;
1621 return MOVE_UI_UPDATE;
1622 }
1623 } else if (tx >= 0 && tx < w && ty == -1) {
1624 ui->drag = 2;
1625 ui->dragnum = state->sequence[tx];
1626 ui->dragpos = tx;
1627 ui->edgepos = FROMCOORD(x + TILESIZE/2);
1628 return MOVE_UI_UPDATE;
1629 } else if (ty >= 0 && ty < w && tx == -1) {
1630 ui->drag = 1;
1631 ui->dragnum = state->sequence[ty];
1632 ui->dragpos = ty;
1633 ui->edgepos = FROMCOORD(y + TILESIZE/2);
1634 return MOVE_UI_UPDATE;
1635 }
1636 } else if (IS_MOUSE_DRAG(button)) {
1637 if (!ui->hpencil &&
1638 tx >= 0 && tx < w && ty >= 0 && ty < w &&
1639 abs(tx - ui->ohx) == abs(ty - ui->ohy)) {
1640 ui->odn = abs(tx - ui->ohx) + 1;
1641 ui->odx = (tx < ui->ohx ? -1 : +1);
1642 ui->ody = (ty < ui->ohy ? -1 : +1);
1643 } else {
1644 ui->odx = ui->ody = 0;
1645 ui->odn = 1;
1646 }
1647 return MOVE_UI_UPDATE;
1648 }
1649
1650 if (IS_CURSOR_MOVE(button)) {
1651 int cx = find_in_sequence(state->sequence, w, ui->hx);
1652 int cy = find_in_sequence(state->sequence, w, ui->hy);
1653 move_cursor(button, &cx, &cy, w, w, false, NULL);
1654 ui->hx = state->sequence[cx];
1655 ui->hy = state->sequence[cy];
1656 ui->hshow = true;
1657 ui->hcursor = true;
1658 ui->ohx = cx;
1659 ui->ohy = cy;
1660 ui->odx = ui->ody = 0;
1661 ui->odn = 1;
1662 return MOVE_UI_UPDATE;
1663 }
1664 if (ui->hshow &&
1665 (button == CURSOR_SELECT)) {
1666 ui->hpencil = !ui->hpencil;
1667 ui->hcursor = true;
1668 return MOVE_UI_UPDATE;
1669 }
1670
1671 if (ui->hshow &&
1672 ((ISCHAR(button) && FROMCHAR(button, state->par.id) <= w) ||
1673 button == CURSOR_SELECT2 || button == '\b')) {
1674 int n = FROMCHAR(button, state->par.id);
1675 int i, buflen;
1676 char *movebuf;
1677
1678 if (button == CURSOR_SELECT2 || button == '\b')
1679 n = 0;
1680
1681 for (i = 0; i < ui->odn; i++) {
1682 int x = state->sequence[ui->ohx + i*ui->odx];
1683 int y = state->sequence[ui->ohy + i*ui->ody];
1684 int index = y*w+x;
1685
1686 /*
1687 * Can't make pencil marks in a filled square. This can only
1688 * become highlighted if we're using cursor keys.
1689 */
1690 if (ui->hpencil && state->grid[index])
1691 return NULL;
1692
1693 /*
1694 * Can't do anything to an immutable square. Exception:
1695 * trying to set it to what it already was is OK (so that
1696 * multifilling can set a whole diagonal to a without
1697 * having to detour round the one immutable square in the
1698 * middle that already said a).
1699 */
1700 if (!ui->hpencil && state->grid[index] == n)
1701 /* OK even if it is immutable */;
1702 else if (state->common->immutable[index])
1703 return NULL;
1704 }
1705
1706 movebuf = snewn(80 * ui->odn, char);
1707 buflen = sprintf(movebuf, "%c%d,%d,%d",
1708 (char)(ui->hpencil && n > 0 ? 'P' : 'R'),
1709 ui->hx, ui->hy, n);
1710 for (i = 1; i < ui->odn; i++) {
1711 assert(buflen < i*80);
1712 buflen += sprintf(movebuf + buflen, "+%d,%d",
1713 state->sequence[ui->ohx + i*ui->odx],
1714 state->sequence[ui->ohy + i*ui->ody]);
1715 }
1716 movebuf = sresize(movebuf, buflen+1, char);
1717
1718 /*
1719 * Hide the highlight after a keypress, if it was mouse-
1720 * generated. Also, don't hide it if this move has changed
1721 * pencil marks and the user preference says not to hide the
1722 * highlight in that situation.
1723 */
1724 if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight))
1725 ui->hshow = false;
1726
1727 return movebuf;
1728 }
1729
1730 if (button == 'M' || button == 'm')
1731 return dupstr("M");
1732
1733 return NULL;
1734}
1735
1736static game_state *execute_move(const game_state *from, const char *move)
1737{
1738 int w = from->par.w, a = w*w;
1739 game_state *ret;
1740 int x, y, i, j, n, pos;
1741
1742 if (move[0] == 'S') {
1743 ret = dup_game(from);
1744 ret->completed = ret->cheated = true;
1745
1746 for (i = 0; i < a; i++) {
1747 if (!ISCHAR(move[i+1]) || FROMCHAR(move[i+1], from->par.id) > w) {
1748 free_game(ret);
1749 return NULL;
1750 }
1751 ret->grid[i] = FROMCHAR(move[i+1], from->par.id);
1752 ret->pencil[i] = 0;
1753 }
1754
1755 if (move[a+1] != '\0') {
1756 free_game(ret);
1757 return NULL;
1758 }
1759
1760 return ret;
1761 } else if ((move[0] == 'P' || move[0] == 'R') &&
1762 sscanf(move+1, "%d,%d,%d%n", &x, &y, &n, &pos) == 3 &&
1763 n >= 0 && n <= w) {
1764 const char *mp = move + 1 + pos;
1765 bool pencil = (move[0] == 'P');
1766 ret = dup_game(from);
1767
1768 while (1) {
1769 if (x < 0 || x >= w || y < 0 || y >= w) {
1770 free_game(ret);
1771 return NULL;
1772 }
1773 if (from->common->immutable[y*w+x] &&
1774 !(!pencil && from->grid[y*w+x] == n))
1775 return NULL;
1776
1777 if (move[0] == 'P' && n > 0) {
1778 ret->pencil[y*w+x] ^= 1 << n;
1779 } else {
1780 ret->grid[y*w+x] = n;
1781 ret->pencil[y*w+x] = 0;
1782 }
1783
1784 if (!*mp)
1785 break;
1786
1787 if (*mp != '+')
1788 return NULL;
1789 if (sscanf(mp, "+%d,%d%n", &x, &y, &pos) < 2)
1790 return NULL;
1791 mp += pos;
1792 }
1793
1794 if (!ret->completed && !check_errors(ret, NULL))
1795 ret->completed = true;
1796
1797 return ret;
1798 } else if (move[0] == 'M') {
1799 /*
1800 * Fill in absolutely all pencil marks everywhere. (I
1801 * wouldn't use this for actual play, but it's a handy
1802 * starting point when following through a set of
1803 * diagnostics output by the standalone solver.)
1804 */
1805 ret = dup_game(from);
1806 for (i = 0; i < a; i++) {
1807 if (!ret->grid[i])
1808 ret->pencil[i] = (1 << (w+1)) - (1 << 1);
1809 }
1810 return ret;
1811 } else if (move[0] == 'D' &&
1812 sscanf(move+1, "%d,%d", &x, &y) == 2) {
1813 /*
1814 * Reorder the rows and columns so that digit x is in position
1815 * y.
1816 */
1817 ret = dup_game(from);
1818 for (i = j = 0; i < w; i++) {
1819 if (i == y) {
1820 ret->sequence[i] = x;
1821 } else {
1822 if (from->sequence[j] == x)
1823 j++;
1824 ret->sequence[i] = from->sequence[j++];
1825 }
1826 }
1827 /*
1828 * Eliminate any obsoleted dividers.
1829 */
1830 for (x = 0; x < w; x++) {
1831 int i = ret->sequence[x];
1832 int j = (x+1 < w ? ret->sequence[x+1] : -1);
1833 if (ret->dividers[i] != j)
1834 ret->dividers[i] = -1;
1835 }
1836 return ret;
1837 } else if (move[0] == 'V' &&
1838 sscanf(move+1, "%d,%d", &i, &j) == 2) {
1839 ret = dup_game(from);
1840 if (ret->dividers[i] == j)
1841 ret->dividers[i] = -1;
1842 else
1843 ret->dividers[i] = j;
1844 return ret;
1845 } else
1846 return NULL; /* couldn't parse move string */
1847}
1848
1849/* ----------------------------------------------------------------------
1850 * Drawing routines.
1851 */
1852
1853#define SIZE(w) ((w) * TILESIZE + 2*BORDER + LEGEND)
1854
1855static void game_compute_size(const game_params *params, int tilesize,
1856 const game_ui *ui, int *x, int *y)
1857{
1858 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1859 struct { int tilesize; } ads, *ds = &ads;
1860 ads.tilesize = tilesize;
1861
1862 *x = *y = SIZE(params->w);
1863}
1864
1865static void game_set_size(drawing *dr, game_drawstate *ds,
1866 const game_params *params, int tilesize)
1867{
1868 ds->tilesize = tilesize;
1869}
1870
1871static float *game_colours(frontend *fe, int *ncolours)
1872{
1873 float *ret = snewn(3 * NCOLOURS, float);
1874
1875 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1876
1877 ret[COL_GRID * 3 + 0] = 0.0F;
1878 ret[COL_GRID * 3 + 1] = 0.0F;
1879 ret[COL_GRID * 3 + 2] = 0.0F;
1880
1881 ret[COL_USER * 3 + 0] = 0.0F;
1882 ret[COL_USER * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1];
1883 ret[COL_USER * 3 + 2] = 0.0F;
1884
1885 ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0];
1886 ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1];
1887 ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2];
1888
1889 ret[COL_ERROR * 3 + 0] = 1.0F;
1890 ret[COL_ERROR * 3 + 1] = 0.0F;
1891 ret[COL_ERROR * 3 + 2] = 0.0F;
1892
1893 ret[COL_PENCIL * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
1894 ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
1895 ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
1896
1897 ret[COL_DIAGONAL * 3 + 0] = 0.95F * ret[COL_BACKGROUND * 3 + 0];
1898 ret[COL_DIAGONAL * 3 + 1] = 0.95F * ret[COL_BACKGROUND * 3 + 1];
1899 ret[COL_DIAGONAL * 3 + 2] = 0.95F * ret[COL_BACKGROUND * 3 + 2];
1900
1901 *ncolours = NCOLOURS;
1902 return ret;
1903}
1904
1905static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1906{
1907 int w = state->par.w, a = w*w;
1908 struct game_drawstate *ds = snew(struct game_drawstate);
1909 int i;
1910
1911 ds->w = w;
1912 ds->par = state->par; /* structure copy */
1913 ds->tilesize = 0;
1914 ds->started = false;
1915 ds->tiles = snewn(a, long);
1916 ds->legend = snewn(w, long);
1917 ds->pencil = snewn(a, long);
1918 ds->errors = snewn(a, long);
1919 ds->sequence = snewn(a, digit);
1920 for (i = 0; i < a; i++)
1921 ds->tiles[i] = ds->pencil[i] = -1;
1922 for (i = 0; i < w; i++)
1923 ds->legend[i] = -1;
1924 ds->errtmp = snewn(a, long);
1925
1926 return ds;
1927}
1928
1929static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1930{
1931 sfree(ds->tiles);
1932 sfree(ds->pencil);
1933 sfree(ds->errors);
1934 sfree(ds->errtmp);
1935 sfree(ds->sequence);
1936 sfree(ds);
1937}
1938
1939static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, long tile,
1940 long pencil, long error)
1941{
1942 int w = ds->w /* , a = w*w */;
1943 int tx, ty, tw, th;
1944 int cx, cy, cw, ch;
1945 char str[64];
1946
1947 tx = BORDER + LEGEND + x * TILESIZE + 1;
1948 ty = BORDER + LEGEND + y * TILESIZE + 1;
1949
1950 cx = tx;
1951 cy = ty;
1952 cw = tw = TILESIZE-1;
1953 ch = th = TILESIZE-1;
1954
1955 if (tile & DF_LEGEND) {
1956 cx += TILESIZE/10;
1957 cy += TILESIZE/10;
1958 cw -= TILESIZE/5;
1959 ch -= TILESIZE/5;
1960 tile |= DF_IMMUTABLE;
1961 }
1962
1963 clip(dr, cx, cy, cw, ch);
1964
1965 /* background needs erasing */
1966 draw_rect(dr, cx, cy, cw, ch,
1967 (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT :
1968 (x == y) ? COL_DIAGONAL : COL_BACKGROUND);
1969
1970 /* dividers */
1971 if (tile & DF_DIVIDER_TOP)
1972 draw_rect(dr, cx, cy, cw, 1, COL_GRID);
1973 if (tile & DF_DIVIDER_BOT)
1974 draw_rect(dr, cx, cy+ch-1, cw, 1, COL_GRID);
1975 if (tile & DF_DIVIDER_LEFT)
1976 draw_rect(dr, cx, cy, 1, ch, COL_GRID);
1977 if (tile & DF_DIVIDER_RIGHT)
1978 draw_rect(dr, cx+cw-1, cy, 1, ch, COL_GRID);
1979
1980 /* pencil-mode highlight */
1981 if (tile & DF_HIGHLIGHT_PENCIL) {
1982 int coords[6];
1983 coords[0] = cx;
1984 coords[1] = cy;
1985 coords[2] = cx+cw/2;
1986 coords[3] = cy;
1987 coords[4] = cx;
1988 coords[5] = cy+ch/2;
1989 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
1990 }
1991
1992 /* new number needs drawing? */
1993 if (tile & DF_DIGIT_MASK) {
1994 str[1] = '\0';
1995 str[0] = TOCHAR(tile & DF_DIGIT_MASK, ds->par.id);
1996 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1997 FONT_VARIABLE, TILESIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE,
1998 (error & EF_LATIN) ? COL_ERROR :
1999 (tile & DF_IMMUTABLE) ? COL_GRID : COL_USER, str);
2000
2001 if (error & EF_LEFT_MASK) {
2002 int a = (error >> (EF_LEFT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
2003 int b = (error >> (EF_LEFT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
2004 int c = (error >> (EF_LEFT_SHIFT ))&EF_DIGIT_MASK;
2005 char buf[10];
2006 sprintf(buf, "(%c%c)%c", TOCHAR(a, ds->par.id),
2007 TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id));
2008 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/6,
2009 FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE,
2010 COL_ERROR, buf);
2011 }
2012 if (error & EF_RIGHT_MASK) {
2013 int a = (error >> (EF_RIGHT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
2014 int b = (error >> (EF_RIGHT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
2015 int c = (error >> (EF_RIGHT_SHIFT ))&EF_DIGIT_MASK;
2016 char buf[10];
2017 sprintf(buf, "%c(%c%c)", TOCHAR(a, ds->par.id),
2018 TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id));
2019 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE - TILESIZE/6,
2020 FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE,
2021 COL_ERROR, buf);
2022 }
2023 } else {
2024 int i, j, npencil;
2025 int pl, pr, pt, pb;
2026 float bestsize;
2027 int pw, ph, minph, pbest, fontsize;
2028
2029 /* Count the pencil marks required. */
2030 for (i = 1, npencil = 0; i <= w; i++)
2031 if (pencil & (1 << i))
2032 npencil++;
2033 if (npencil) {
2034
2035 minph = 2;
2036
2037 /*
2038 * Determine the bounding rectangle within which we're going
2039 * to put the pencil marks.
2040 */
2041 /* Start with the whole square */
2042 pl = tx + GRIDEXTRA;
2043 pr = pl + TILESIZE - GRIDEXTRA;
2044 pt = ty + GRIDEXTRA;
2045 pb = pt + TILESIZE - GRIDEXTRA;
2046
2047 /*
2048 * We arrange our pencil marks in a grid layout, with
2049 * the number of rows and columns adjusted to allow the
2050 * maximum font size.
2051 *
2052 * So now we work out what the grid size ought to be.
2053 */
2054 bestsize = 0.0;
2055 pbest = 0;
2056 /* Minimum */
2057 for (pw = 3; pw < max(npencil,4); pw++) {
2058 float fw, fh, fs;
2059
2060 ph = (npencil + pw - 1) / pw;
2061 ph = max(ph, minph);
2062 fw = (pr - pl) / (float)pw;
2063 fh = (pb - pt) / (float)ph;
2064 fs = min(fw, fh);
2065 if (fs > bestsize) {
2066 bestsize = fs;
2067 pbest = pw;
2068 }
2069 }
2070 assert(pbest > 0);
2071 pw = pbest;
2072 ph = (npencil + pw - 1) / pw;
2073 ph = max(ph, minph);
2074
2075 /*
2076 * Now we've got our grid dimensions, work out the pixel
2077 * size of a grid element, and round it to the nearest
2078 * pixel. (We don't want rounding errors to make the
2079 * grid look uneven at low pixel sizes.)
2080 */
2081 fontsize = min((pr - pl) / pw, (pb - pt) / ph);
2082
2083 /*
2084 * Centre the resulting figure in the square.
2085 */
2086 pl = tx + (TILESIZE - fontsize * pw) / 2;
2087 pt = ty + (TILESIZE - fontsize * ph) / 2;
2088
2089 /*
2090 * Now actually draw the pencil marks.
2091 */
2092 for (i = 1, j = 0; i <= w; i++)
2093 if (pencil & (1 << i)) {
2094 int dx = j % pw, dy = j / pw;
2095
2096 str[1] = '\0';
2097 str[0] = TOCHAR(i, ds->par.id);
2098 draw_text(dr, pl + fontsize * (2*dx+1) / 2,
2099 pt + fontsize * (2*dy+1) / 2,
2100 FONT_VARIABLE, fontsize,
2101 ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str);
2102 j++;
2103 }
2104 }
2105 }
2106
2107 unclip(dr);
2108
2109 draw_update(dr, cx, cy, cw, ch);
2110}
2111
2112static void game_redraw(drawing *dr, game_drawstate *ds,
2113 const game_state *oldstate, const game_state *state,
2114 int dir, const game_ui *ui,
2115 float animtime, float flashtime)
2116{
2117 int w = state->par.w /*, a = w*w */;
2118 int x, y, i, j;
2119
2120 if (!ds->started) {
2121 /*
2122 * Big containing rectangle.
2123 */
2124 draw_rect(dr, COORD(0) - GRIDEXTRA, COORD(0) - GRIDEXTRA,
2125 w*TILESIZE+1+GRIDEXTRA*2, w*TILESIZE+1+GRIDEXTRA*2,
2126 COL_GRID);
2127
2128 draw_update(dr, 0, 0, SIZE(w), SIZE(w));
2129
2130 ds->started = true;
2131 }
2132
2133 check_errors(state, ds->errtmp);
2134
2135 /*
2136 * Construct a modified version of state->sequence which takes
2137 * into account an unfinished drag operation.
2138 */
2139 if (ui->drag) {
2140 x = ui->dragnum;
2141 y = ui->dragpos;
2142 } else {
2143 x = y = -1;
2144 }
2145 for (i = j = 0; i < w; i++) {
2146 if (i == y) {
2147 ds->sequence[i] = x;
2148 } else {
2149 if (state->sequence[j] == x)
2150 j++;
2151 ds->sequence[i] = state->sequence[j++];
2152 }
2153 }
2154
2155 /*
2156 * Draw the table legend.
2157 */
2158 for (x = 0; x < w; x++) {
2159 int sx = ds->sequence[x];
2160 long tile = (sx+1) | DF_LEGEND;
2161 if (ds->legend[x] != tile) {
2162 ds->legend[x] = tile;
2163 draw_tile(dr, ds, -1, x, tile, 0, 0);
2164 draw_tile(dr, ds, x, -1, tile, 0, 0);
2165 }
2166 }
2167
2168 for (y = 0; y < w; y++) {
2169 int sy = ds->sequence[y];
2170 for (x = 0; x < w; x++) {
2171 long tile = 0L, pencil = 0L, error;
2172 int sx = ds->sequence[x];
2173
2174 if (state->grid[sy*w+sx])
2175 tile = state->grid[sy*w+sx];
2176 else
2177 pencil = (long)state->pencil[sy*w+sx];
2178
2179 if (state->common->immutable[sy*w+sx])
2180 tile |= DF_IMMUTABLE;
2181
2182 if ((ui->drag == 5 && ui->dragnum == sy) ||
2183 (ui->drag == 6 && ui->dragnum == sx)) {
2184 tile |= DF_HIGHLIGHT;
2185 } else if (ui->hshow) {
2186 int i = abs(x - ui->ohx);
2187 bool highlight = false;
2188 if (ui->odn > 1) {
2189 /*
2190 * When a diagonal multifill selection is shown,
2191 * we show it in its original grid position
2192 * regardless of in-progress row/col drags. Moving
2193 * every square about would be horrible.
2194 */
2195 if (i >= 0 && i < ui->odn &&
2196 x == ui->ohx + i*ui->odx &&
2197 y == ui->ohy + i*ui->ody)
2198 highlight = true;
2199 } else {
2200 /*
2201 * For a single square, we move its highlight
2202 * around with the drag.
2203 */
2204 highlight = (ui->hx == sx && ui->hy == sy);
2205 }
2206 if (highlight)
2207 tile |= (ui->hpencil ? DF_HIGHLIGHT_PENCIL : DF_HIGHLIGHT);
2208 }
2209
2210 if (flashtime > 0 &&
2211 (flashtime <= FLASH_TIME/3 ||
2212 flashtime >= FLASH_TIME*2/3))
2213 tile |= DF_HIGHLIGHT; /* completion flash */
2214
2215 if (y <= 0 || state->dividers[ds->sequence[y-1]] == sy)
2216 tile |= DF_DIVIDER_TOP;
2217 if (y+1 >= w || state->dividers[sy] == ds->sequence[y+1])
2218 tile |= DF_DIVIDER_BOT;
2219 if (x <= 0 || state->dividers[ds->sequence[x-1]] == sx)
2220 tile |= DF_DIVIDER_LEFT;
2221 if (x+1 >= w || state->dividers[sx] == ds->sequence[x+1])
2222 tile |= DF_DIVIDER_RIGHT;
2223
2224 error = ds->errtmp[sy*w+sx];
2225
2226 if (ds->tiles[y*w+x] != tile ||
2227 ds->pencil[y*w+x] != pencil ||
2228 ds->errors[y*w+x] != error) {
2229 ds->tiles[y*w+x] = tile;
2230 ds->pencil[y*w+x] = pencil;
2231 ds->errors[y*w+x] = error;
2232 draw_tile(dr, ds, x, y, tile, pencil, error);
2233 }
2234 }
2235 }
2236}
2237
2238static float game_anim_length(const game_state *oldstate,
2239 const game_state *newstate, int dir, game_ui *ui)
2240{
2241 return 0.0F;
2242}
2243
2244static float game_flash_length(const game_state *oldstate,
2245 const game_state *newstate, int dir, game_ui *ui)
2246{
2247 if (!oldstate->completed && newstate->completed &&
2248 !oldstate->cheated && !newstate->cheated)
2249 return FLASH_TIME;
2250 return 0.0F;
2251}
2252
2253static void game_get_cursor_location(const game_ui *ui,
2254 const game_drawstate *ds,
2255 const game_state *state,
2256 const game_params *params,
2257 int *x, int *y, int *w, int *h)
2258{
2259}
2260
2261static int game_status(const game_state *state)
2262{
2263 return state->completed ? +1 : 0;
2264}
2265
2266static bool game_timing_state(const game_state *state, game_ui *ui)
2267{
2268 if (state->completed)
2269 return false;
2270 return true;
2271}
2272
2273static void game_print_size(const game_params *params, const game_ui *ui,
2274 float *x, float *y)
2275{
2276 int pw, ph;
2277
2278 /*
2279 * We use 9mm squares by default, like Solo.
2280 */
2281 game_compute_size(params, 900, ui, &pw, &ph);
2282 *x = pw / 100.0F;
2283 *y = ph / 100.0F;
2284}
2285
2286static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2287 int tilesize)
2288{
2289 int w = state->par.w;
2290 int ink = print_mono_colour(dr, 0);
2291 int x, y;
2292
2293 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2294 game_drawstate ads, *ds = &ads;
2295 game_set_size(dr, ds, NULL, tilesize);
2296
2297 /*
2298 * Border.
2299 */
2300 print_line_width(dr, 3 * TILESIZE / 40);
2301 draw_rect_outline(dr, BORDER + LEGEND, BORDER + LEGEND,
2302 w*TILESIZE, w*TILESIZE, ink);
2303
2304 /*
2305 * Legend on table.
2306 */
2307 for (x = 0; x < w; x++) {
2308 char str[2];
2309 str[1] = '\0';
2310 str[0] = TOCHAR(x+1, state->par.id);
2311 draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2,
2312 BORDER + TILESIZE/2,
2313 FONT_VARIABLE, TILESIZE/2,
2314 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
2315 draw_text(dr, BORDER + TILESIZE/2,
2316 BORDER+LEGEND + x*TILESIZE + TILESIZE/2,
2317 FONT_VARIABLE, TILESIZE/2,
2318 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
2319 }
2320
2321 /*
2322 * Main grid.
2323 */
2324 for (x = 1; x < w; x++) {
2325 print_line_width(dr, TILESIZE / 40);
2326 draw_line(dr, BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND,
2327 BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND+w*TILESIZE, ink);
2328 }
2329 for (y = 1; y < w; y++) {
2330 print_line_width(dr, TILESIZE / 40);
2331 draw_line(dr, BORDER+LEGEND, BORDER+LEGEND+y*TILESIZE,
2332 BORDER+LEGEND+w*TILESIZE, BORDER+LEGEND+y*TILESIZE, ink);
2333 }
2334
2335 /*
2336 * Numbers.
2337 */
2338 for (y = 0; y < w; y++)
2339 for (x = 0; x < w; x++)
2340 if (state->grid[y*w+x]) {
2341 char str[2];
2342 str[1] = '\0';
2343 str[0] = TOCHAR(state->grid[y*w+x], state->par.id);
2344 draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2,
2345 BORDER+LEGEND + y*TILESIZE + TILESIZE/2,
2346 FONT_VARIABLE, TILESIZE/2,
2347 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
2348 }
2349}
2350
2351#ifdef COMBINED
2352#define thegame group
2353#endif
2354
2355const struct game thegame = {
2356 "Group", NULL, NULL,
2357 default_params,
2358 game_fetch_preset, NULL,
2359 decode_params,
2360 encode_params,
2361 free_params,
2362 dup_params,
2363 true, game_configure, custom_params,
2364 validate_params,
2365 new_game_desc,
2366 validate_desc,
2367 new_game,
2368 dup_game,
2369 free_game,
2370 true, solve_game,
2371 true, game_can_format_as_text_now, game_text_format,
2372 get_prefs, set_prefs,
2373 new_ui,
2374 free_ui,
2375 NULL, /* encode_ui */
2376 NULL, /* decode_ui */
2377 NULL, /* game_request_keys */
2378 game_changed_state,
2379 current_key_label,
2380 interpret_move,
2381 execute_move,
2382 PREFERRED_TILESIZE, game_compute_size, game_set_size,
2383 game_colours,
2384 game_new_drawstate,
2385 game_free_drawstate,
2386 game_redraw,
2387 game_anim_length,
2388 game_flash_length,
2389 game_get_cursor_location,
2390 game_status,
2391 true, false, game_print_size, game_print,
2392 false, /* wants_statusbar */
2393 false, game_timing_state,
2394 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
2395};
2396
2397#ifdef STANDALONE_SOLVER
2398
2399#include <stdarg.h>
2400
2401int main(int argc, char **argv)
2402{
2403 game_params *p;
2404 game_state *s;
2405 char *id = NULL, *desc;
2406 const char *err;
2407 digit *grid;
2408 bool grade = false;
2409 int ret, diff;
2410 bool really_show_working = false;
2411
2412 while (--argc > 0) {
2413 char *p = *++argv;
2414 if (!strcmp(p, "-v")) {
2415 really_show_working = true;
2416 } else if (!strcmp(p, "-g")) {
2417 grade = true;
2418 } else if (*p == '-') {
2419 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2420 return 1;
2421 } else {
2422 id = p;
2423 }
2424 }
2425
2426 if (!id) {
2427 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
2428 return 1;
2429 }
2430
2431 desc = strchr(id, ':');
2432 if (!desc) {
2433 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2434 return 1;
2435 }
2436 *desc++ = '\0';
2437
2438 p = default_params();
2439 decode_params(p, id);
2440 err = validate_desc(p, desc);
2441 if (err) {
2442 fprintf(stderr, "%s: %s\n", argv[0], err);
2443 return 1;
2444 }
2445 s = new_game(NULL, p, desc);
2446
2447 grid = snewn(p->w * p->w, digit);
2448
2449 /*
2450 * When solving a Normal puzzle, we don't want to bother the
2451 * user with Hard-level deductions. For this reason, we grade
2452 * the puzzle internally before doing anything else.
2453 */
2454 ret = -1; /* placate optimiser */
2455 solver_show_working = 0;
2456 for (diff = 0; diff < DIFFCOUNT; diff++) {
2457 memcpy(grid, s->grid, p->w * p->w);
2458 ret = solver(&s->par, grid, diff);
2459 if (ret <= diff)
2460 break;
2461 }
2462
2463 if (diff == DIFFCOUNT) {
2464 if (really_show_working) {
2465 solver_show_working = true;
2466 memcpy(grid, s->grid, p->w * p->w);
2467 ret = solver(&s->par, grid, DIFFCOUNT - 1);
2468 }
2469 if (grade)
2470 printf("Difficulty rating: ambiguous\n");
2471 else
2472 printf("Unable to find a unique solution\n");
2473 } else {
2474 if (grade) {
2475 if (ret == diff_impossible)
2476 printf("Difficulty rating: impossible (no solution exists)\n");
2477 else
2478 printf("Difficulty rating: %s\n", group_diffnames[ret]);
2479 } else {
2480 solver_show_working = really_show_working;
2481 memcpy(grid, s->grid, p->w * p->w);
2482 ret = solver(&s->par, grid, diff);
2483 if (ret != diff)
2484 printf("Puzzle is inconsistent\n");
2485 else {
2486 memcpy(s->grid, grid, p->w * p->w);
2487 fputs(game_text_format(s), stdout);
2488 }
2489 }
2490 }
2491
2492 return 0;
2493}
2494
2495#endif
2496
2497/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/unfinished/group.gap b/apps/plugins/puzzles/src/unfinished/group.gap
new file mode 100644
index 0000000000..280adf4664
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/group.gap
@@ -0,0 +1,97 @@
1# run this file with
2# gap -b -q < /dev/null group.gap | perl -pe 's/\\\n//s' | indent -kr
3
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..cf919922e7
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/numgame.c
@@ -0,0 +1,1294 @@
1/*
2 * This program implements a breadth-first search which
3 * exhaustively solves the Countdown numbers game, and related
4 * games with slightly different rule sets such as `Flippo'.
5 *
6 * Currently it is simply a standalone command-line utility to
7 * which you provide a set of numbers and it tells you everything
8 * it can make together with how many different ways it can be
9 * made. I would like ultimately to turn it into the generator for
10 * a Puzzles puzzle, but I haven't even started on writing a
11 * Puzzles user interface yet.
12 */
13
14/*
15 * TODO:
16 *
17 * - start thinking about difficulty ratings
18 * + anything involving associative operations will be flagged
19 * as many-paths because of the associative options (e.g.
20 * 2*3*4 can be (2*3)*4 or 2*(3*4), or indeed (2*4)*3). This
21 * is probably a _good_ thing, since those are unusually
22 * easy.
23 * + tree-structured calculations ((a*b)/(c+d)) have multiple
24 * paths because the independent branches of the tree can be
25 * evaluated in either order, whereas straight-line
26 * calculations with no branches will be considered easier.
27 * Can we do anything about this? It's certainly not clear to
28 * me that tree-structure calculations are _easier_, although
29 * I'm also not convinced they're harder.
30 * + I think for a realistic difficulty assessment we must also
31 * consider the `obviousness' of the arithmetic operations in
32 * some heuristic sense, and also (in Countdown) how many
33 * numbers ended up being used.
34 * - actually try some generations
35 * - at this point we're probably ready to start on the Puzzles
36 * integration.
37 */
38
39#include <stdio.h>
40#include <string.h>
41#include <limits.h>
42#include <assert.h>
43#ifdef NO_TGMATH_H
44# include <math.h>
45#else
46# include <tgmath.h>
47#endif
48
49#include "puzzles.h"
50#include "tree234.h"
51
52/*
53 * To search for numbers we can make, we employ a breadth-first
54 * search across the space of sets of input numbers. That is, for
55 * example, we start with the set (3,6,25,50,75,100); we apply
56 * moves which involve combining two numbers (e.g. adding the 50
57 * and the 75 takes us to the set (3,6,25,100,125); and then we see
58 * if we ever end up with a set containing (say) 952.
59 *
60 * If the rules are changed so that all the numbers must be used,
61 * this is easy to adjust to: we simply see if we end up with a set
62 * containing _only_ (say) 952.
63 *
64 * Obviously, we can vary the rules about permitted arithmetic
65 * operations simply by altering the set of valid moves in the bfs.
66 * However, there's one common rule in this sort of puzzle which
67 * takes a little more thought, and that's _concatenation_. For
68 * example, if you are given (say) four 4s and required to make 10,
69 * you are permitted to combine two of the 4s into a 44 to begin
70 * with, making (44-4)/4 = 10. However, you are generally not
71 * allowed to concatenate two numbers that _weren't_ both in the
72 * original input set (you couldn't multiply two 4s to get 16 and
73 * then concatenate a 4 on to it to make 164), so concatenation is
74 * not an operation which is valid in all situations.
75 *
76 * We could enforce this restriction by storing a flag alongside
77 * each number indicating whether or not it's an original number;
78 * the rules being that concatenation of two numbers is only valid
79 * if they both have the original flag, and that its output _also_
80 * has the original flag (so that you can concatenate three 4s into
81 * a 444), but that applying any other arithmetic operation clears
82 * the original flag on the output. However, we can get marginally
83 * simpler than that by observing that since concatenation has to
84 * happen to a number before any other operation, we can simply
85 * place all the concatenations at the start of the search. In
86 * other words, we have a global flag on an entire number _set_
87 * which indicates whether we are still permitted to perform
88 * concatenations; if so, we can concatenate any of the numbers in
89 * that set. Performing any other operation clears the flag.
90 */
91
92#define SETFLAG_CONCAT 1 /* we can do concatenation */
93
94struct sets;
95
96struct ancestor {
97 struct set *prev; /* index of ancestor set in set list */
98 unsigned char pa, pb, po, pr; /* operation that got here from prev */
99};
100
101struct set {
102 int *numbers; /* rationals stored as n,d pairs */
103 short nnumbers; /* # of rationals, so half # of ints */
104 short flags; /* SETFLAG_CONCAT only, at present */
105 int npaths; /* number of ways to reach this set */
106 struct ancestor a; /* primary ancestor */
107 struct ancestor *as; /* further ancestors, if we care */
108 int nas, assize;
109};
110
111struct output {
112 int number;
113 struct set *set;
114 int index; /* which number in the set is it? */
115 int npaths; /* number of ways to reach this */
116};
117
118#define SETLISTLEN 1024
119#define NUMBERLISTLEN 32768
120#define OUTPUTLISTLEN 1024
121struct operation;
122struct sets {
123 struct set **setlists;
124 int nsets, nsetlists, setlistsize;
125 tree234 *settree;
126 int **numberlists;
127 int nnumbers, nnumberlists, numberlistsize;
128 struct output **outputlists;
129 int noutputs, noutputlists, outputlistsize;
130 tree234 *outputtree;
131 const struct operation *const *ops;
132};
133
134#define OPFLAG_NEEDS_CONCAT 1
135#define OPFLAG_KEEPS_CONCAT 2
136#define OPFLAG_UNARY 4
137#define OPFLAG_UNARYPREFIX 8
138#define OPFLAG_FN 16
139
140struct operation {
141 /*
142 * Most operations should be shown in the output working, but
143 * concatenation should not; we just take the result of the
144 * concatenation and assume that it's obvious how it was
145 * derived.
146 */
147 int display;
148
149 /*
150 * Text display of the operator, in expressions and for
151 * debugging respectively.
152 */
153 const char *text, *dbgtext;
154
155 /*
156 * Flags dictating when the operator can be applied.
157 */
158 int flags;
159
160 /*
161 * Priority of the operator (for avoiding unnecessary
162 * parentheses when formatting it into a string).
163 */
164 int priority;
165
166 /*
167 * Associativity of the operator. Bit 0 means we need parens
168 * when the left operand of one of these operators is another
169 * instance of it, e.g. (2^3)^4. Bit 1 means we need parens
170 * when the right operand is another instance of the same
171 * operator, e.g. 2-(3-4). Thus:
172 *
173 * - this field is 0 for a fully associative operator, since
174 * we never need parens.
175 * - it's 1 for a right-associative operator.
176 * - it's 2 for a left-associative operator.
177 * - it's 3 for a _non_-associative operator (which always
178 * uses parens just to be sure).
179 */
180 int assoc;
181
182 /*
183 * Whether the operator is commutative. Saves time in the
184 * search if we don't have to try it both ways round.
185 */
186 int commutes;
187
188 /*
189 * Function which implements the operator. Returns true on
190 * success, false on failure. Takes two rationals and writes
191 * out a third.
192 */
193 int (*perform)(int *a, int *b, int *output);
194};
195
196struct rules {
197 const struct operation *const *ops;
198 int use_all;
199};
200
201#define MUL(r, a, b) do { \
202 (r) = (a) * (b); \
203 if ((b) && (a) && (r) / (b) != (a)) return false; \
204} while (0)
205
206#define ADD(r, a, b) do { \
207 (r) = (a) + (b); \
208 if ((a) > 0 && (b) > 0 && (r) < 0) return false; \
209 if ((a) < 0 && (b) < 0 && (r) > 0) return false; \
210} while (0)
211
212#define OUT(output, n, d) do { \
213 int g = gcd((n),(d)); \
214 if (g < 0) g = -g; \
215 if ((d) < 0) g = -g; \
216 if (g == -1 && (n) < -INT_MAX) return false; \
217 if (g == -1 && (d) < -INT_MAX) return false; \
218 (output)[0] = (n)/g; \
219 (output)[1] = (d)/g; \
220 assert((output)[1] > 0); \
221} while (0)
222
223static int gcd(int x, int y)
224{
225 while (x != 0 && y != 0) {
226 int t = x;
227 x = y;
228 y = t % y;
229 }
230
231 return abs(x + y); /* i.e. whichever one isn't zero */
232}
233
234static int perform_add(int *a, int *b, int *output)
235{
236 int at, bt, tn, bn;
237 /*
238 * a0/a1 + b0/b1 = (a0*b1 + b0*a1) / (a1*b1)
239 */
240 MUL(at, a[0], b[1]);
241 MUL(bt, b[0], a[1]);
242 ADD(tn, at, bt);
243 MUL(bn, a[1], b[1]);
244 OUT(output, tn, bn);
245 return true;
246}
247
248static int perform_sub(int *a, int *b, int *output)
249{
250 int at, bt, tn, bn;
251 /*
252 * a0/a1 - b0/b1 = (a0*b1 - b0*a1) / (a1*b1)
253 */
254 MUL(at, a[0], b[1]);
255 MUL(bt, b[0], a[1]);
256 ADD(tn, at, -bt);
257 MUL(bn, a[1], b[1]);
258 OUT(output, tn, bn);
259 return true;
260}
261
262static int perform_mul(int *a, int *b, int *output)
263{
264 int tn, bn;
265 /*
266 * a0/a1 * b0/b1 = (a0*b0) / (a1*b1)
267 */
268 MUL(tn, a[0], b[0]);
269 MUL(bn, a[1], b[1]);
270 OUT(output, tn, bn);
271 return true;
272}
273
274static int perform_div(int *a, int *b, int *output)
275{
276 int tn, bn;
277
278 /*
279 * Division by zero is outlawed.
280 */
281 if (b[0] == 0)
282 return false;
283
284 /*
285 * a0/a1 / b0/b1 = (a0*b1) / (a1*b0)
286 */
287 MUL(tn, a[0], b[1]);
288 MUL(bn, a[1], b[0]);
289 OUT(output, tn, bn);
290 return true;
291}
292
293static int perform_exact_div(int *a, int *b, int *output)
294{
295 int tn, bn;
296
297 /*
298 * Division by zero is outlawed.
299 */
300 if (b[0] == 0)
301 return false;
302
303 /*
304 * a0/a1 / b0/b1 = (a0*b1) / (a1*b0)
305 */
306 MUL(tn, a[0], b[1]);
307 MUL(bn, a[1], b[0]);
308 OUT(output, tn, bn);
309
310 /*
311 * Exact division means we require the result to be an integer.
312 */
313 return (output[1] == 1);
314}
315
316static int max_p10(int n, int *p10_r)
317{
318 /*
319 * Find the smallest power of ten strictly greater than n.
320 *
321 * Special case: we must return at least 10, even if n is
322 * zero. (This is because this function is used for finding
323 * the power of ten by which to multiply a number being
324 * concatenated to the front of n, and concatenating 1 to 0
325 * should yield 10 and not 1.)
326 */
327 int p10 = 10;
328 while (p10 <= (INT_MAX/10) && p10 <= n)
329 p10 *= 10;
330 if (p10 > INT_MAX/10)
331 return false; /* integer overflow */
332 *p10_r = p10;
333 return true;
334}
335
336static int perform_concat(int *a, int *b, int *output)
337{
338 int t1, t2, p10;
339
340 /*
341 * We can't concatenate anything which isn't a non-negative
342 * integer.
343 */
344 if (a[1] != 1 || b[1] != 1 || a[0] < 0 || b[0] < 0)
345 return false;
346
347 /*
348 * For concatenation, we can safely assume leading zeroes
349 * aren't an issue. It isn't clear whether they `should' be
350 * allowed, but it turns out not to matter: concatenating a
351 * leading zero on to a number in order to harmlessly get rid
352 * of the zero is never necessary because unwanted zeroes can
353 * be disposed of by adding them to something instead. So we
354 * disallow them always.
355 *
356 * The only other possibility is that you might want to
357 * concatenate a leading zero on to something and then
358 * concatenate another non-zero digit on to _that_ (to make,
359 * for example, 106); but that's also unnecessary, because you
360 * can make 106 just as easily by concatenating the 0 on to the
361 * _end_ of the 1 first.
362 */
363 if (a[0] == 0)
364 return false;
365
366 if (!max_p10(b[0], &p10)) return false;
367
368 MUL(t1, p10, a[0]);
369 ADD(t2, t1, b[0]);
370 OUT(output, t2, 1);
371 return true;
372}
373
374#define IPOW(ret, x, y) do { \
375 int ipow_limit = (y); \
376 if ((x) == 1 || (x) == 0) ipow_limit = 1; \
377 else if ((x) == -1) ipow_limit &= 1; \
378 (ret) = 1; \
379 while (ipow_limit-- > 0) { \
380 int tmp; \
381 MUL(tmp, ret, x); \
382 ret = tmp; \
383 } \
384} while (0)
385
386static int perform_exp(int *a, int *b, int *output)
387{
388 int an, ad, xn, xd;
389
390 /*
391 * Exponentiation is permitted if the result is rational. This
392 * means that:
393 *
394 * - first we see whether we can take the (denominator-of-b)th
395 * root of a and get a rational; if not, we give up.
396 *
397 * - then we do take that root of a
398 *
399 * - then we multiply by itself (numerator-of-b) times.
400 */
401 if (b[1] > 1) {
402 an = (int)(0.5 + pow(a[0], 1.0/b[1]));
403 ad = (int)(0.5 + pow(a[1], 1.0/b[1]));
404 IPOW(xn, an, b[1]);
405 IPOW(xd, ad, b[1]);
406 if (xn != a[0] || xd != a[1])
407 return false;
408 } else {
409 an = a[0];
410 ad = a[1];
411 }
412 if (b[0] >= 0) {
413 IPOW(xn, an, b[0]);
414 IPOW(xd, ad, b[0]);
415 } else {
416 IPOW(xd, an, -b[0]);
417 IPOW(xn, ad, -b[0]);
418 }
419 if (xd == 0)
420 return false;
421
422 OUT(output, xn, xd);
423 return true;
424}
425
426static int perform_factorial(int *a, int *b, int *output)
427{
428 int ret, t, i;
429
430 /*
431 * Factorials of non-negative integers are permitted.
432 */
433 if (a[1] != 1 || a[0] < 0)
434 return false;
435
436 /*
437 * However, a special case: we don't take a factorial of
438 * anything which would thereby remain the same.
439 */
440 if (a[0] == 1 || a[0] == 2)
441 return false;
442
443 ret = 1;
444 for (i = 1; i <= a[0]; i++) {
445 MUL(t, ret, i);
446 ret = t;
447 }
448
449 OUT(output, ret, 1);
450 return true;
451}
452
453static int perform_decimal(int *a, int *b, int *output)
454{
455 int p10;
456
457 /*
458 * Add a decimal digit to the front of a number;
459 * fail if it's not an integer.
460 * So, 1 --> 0.1, 15 --> 0.15,
461 * or, rather, 1 --> 1/10, 15 --> 15/100,
462 * x --> x / (smallest power of 10 > than x)
463 *
464 */
465 if (a[1] != 1) return false;
466
467 if (!max_p10(a[0], &p10)) return false;
468
469 OUT(output, a[0], p10);
470 return true;
471}
472
473static int perform_recur(int *a, int *b, int *output)
474{
475 int p10, tn, bn;
476
477 /*
478 * This converts a number like .4 to .44444..., or .45 to .45454...
479 * The input number must be -1 < a < 1.
480 *
481 * Calculate the smallest power of 10 that divides the denominator exactly,
482 * returning if no such power of 10 exists. Then multiply the numerator
483 * up accordingly, and the new denominator becomes that power of 10 - 1.
484 */
485 if (abs(a[0]) >= abs(a[1])) return false; /* -1 < a < 1 */
486
487 p10 = 10;
488 while (p10 <= (INT_MAX/10)) {
489 if ((a[1] <= p10) && (p10 % a[1]) == 0) goto found;
490 p10 *= 10;
491 }
492 return false;
493found:
494 tn = a[0] * (p10 / a[1]);
495 bn = p10 - 1;
496
497 OUT(output, tn, bn);
498 return true;
499}
500
501static int perform_root(int *a, int *b, int *output)
502{
503 /*
504 * A root B is: 1 iff a == 0
505 * B ^ (1/A) otherwise
506 */
507 int ainv[2], res;
508
509 if (a[0] == 0) {
510 OUT(output, 1, 1);
511 return true;
512 }
513
514 OUT(ainv, a[1], a[0]);
515 res = perform_exp(b, ainv, output);
516 return res;
517}
518
519static int perform_perc(int *a, int *b, int *output)
520{
521 if (a[0] == 0) return false; /* 0% = 0, uninteresting. */
522 if (a[1] > (INT_MAX/100)) return false;
523
524 OUT(output, a[0], a[1]*100);
525 return true;
526}
527
528static int perform_gamma(int *a, int *b, int *output)
529{
530 int asub1[2];
531
532 /*
533 * gamma(a) = (a-1)!
534 *
535 * special case not caught by perform_fact: gamma(1) is 1 so
536 * don't bother.
537 */
538 if (a[0] == 1 && a[1] == 1) return false;
539
540 OUT(asub1, a[0]-a[1], a[1]);
541 return perform_factorial(asub1, b, output);
542}
543
544static int perform_sqrt(int *a, int *b, int *output)
545{
546 int half[2] = { 1, 2 };
547
548 /*
549 * sqrt(0) == 0, sqrt(1) == 1: don't perform unary noops.
550 */
551 if (a[0] == 0 || (a[0] == 1 && a[1] == 1)) return false;
552
553 return perform_exp(a, half, output);
554}
555
556static const struct operation op_add = {
557 true, "+", "+", 0, 10, 0, true, perform_add
558};
559static const struct operation op_sub = {
560 true, "-", "-", 0, 10, 2, false, perform_sub
561};
562static const struct operation op_mul = {
563 true, "*", "*", 0, 20, 0, true, perform_mul
564};
565static const struct operation op_div = {
566 true, "/", "/", 0, 20, 2, false, perform_div
567};
568static const struct operation op_xdiv = {
569 true, "/", "/", 0, 20, 2, false, perform_exact_div
570};
571static const struct operation op_concat = {
572 false, "", "concat", OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT,
573 1000, 0, false, perform_concat
574};
575static const struct operation op_exp = {
576 true, "^", "^", 0, 30, 1, false, perform_exp
577};
578static const struct operation op_factorial = {
579 true, "!", "!", OPFLAG_UNARY, 40, 0, false, perform_factorial
580};
581static const struct operation op_decimal = {
582 true, ".", ".", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT, 50, 0, false, perform_decimal
583};
584static const struct operation op_recur = {
585 true, "...", "recur", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 2, false, perform_recur
586};
587static const struct operation op_root = {
588 true, "v~", "root", 0, 30, 1, false, perform_root
589};
590static const struct operation op_perc = {
591 true, "%", "%", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 1, false, perform_perc
592};
593static const struct operation op_gamma = {
594 true, "gamma", "gamma", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_FN, 1, 3, false, perform_gamma
595};
596static const struct operation op_sqrt = {
597 true, "v~", "sqrt", OPFLAG_UNARY | OPFLAG_UNARYPREFIX, 30, 1, false, perform_sqrt
598};
599
600/*
601 * In Countdown, divisions resulting in fractions are disallowed.
602 * http://www.askoxford.com/wordgames/countdown/rules/
603 */
604static const struct operation *const ops_countdown[] = {
605 &op_add, &op_mul, &op_sub, &op_xdiv, NULL
606};
607static const struct rules rules_countdown = {
608 ops_countdown, false
609};
610
611/*
612 * A slightly different rule set which handles the reasonably well
613 * known puzzle of making 24 using two 3s and two 8s. For this we
614 * need rational rather than integer division.
615 */
616static const struct operation *const ops_3388[] = {
617 &op_add, &op_mul, &op_sub, &op_div, NULL
618};
619static const struct rules rules_3388 = {
620 ops_3388, true
621};
622
623/*
624 * A still more permissive rule set usable for the four-4s problem
625 * and similar things. Permits concatenation.
626 */
627static const struct operation *const ops_four4s[] = {
628 &op_add, &op_mul, &op_sub, &op_div, &op_concat, NULL
629};
630static const struct rules rules_four4s = {
631 ops_four4s, true
632};
633
634/*
635 * The most permissive ruleset I can think of. Permits
636 * exponentiation, and also silly unary operators like factorials.
637 */
638static const struct operation *const ops_anythinggoes[] = {
639 &op_add, &op_mul, &op_sub, &op_div, &op_concat, &op_exp, &op_factorial,
640 &op_decimal, &op_recur, &op_root, &op_perc, &op_gamma, &op_sqrt, NULL
641};
642static const struct rules rules_anythinggoes = {
643 ops_anythinggoes, true
644};
645
646#define ratcmp(a,op,b) ( (long long)(a)[0] * (b)[1] op \
647 (long long)(b)[0] * (a)[1] )
648
649static int addtoset(struct set *set, int newnumber[2])
650{
651 int i, j;
652
653 /* Find where we want to insert the new number */
654 for (i = 0; i < set->nnumbers &&
655 ratcmp(set->numbers+2*i, <, newnumber); i++);
656
657 /* Move everything else up */
658 for (j = set->nnumbers; j > i; j--) {
659 set->numbers[2*j] = set->numbers[2*j-2];
660 set->numbers[2*j+1] = set->numbers[2*j-1];
661 }
662
663 /* Insert the new number */
664 set->numbers[2*i] = newnumber[0];
665 set->numbers[2*i+1] = newnumber[1];
666
667 set->nnumbers++;
668
669 return i;
670}
671
672#define ensure(array, size, newlen, type) do { \
673 if ((newlen) > (size)) { \
674 (size) = (newlen) + 512; \
675 (array) = sresize((array), (size), type); \
676 } \
677} while (0)
678
679static int setcmp(void *av, void *bv)
680{
681 struct set *a = (struct set *)av;
682 struct set *b = (struct set *)bv;
683 int i;
684
685 if (a->nnumbers < b->nnumbers)
686 return -1;
687 else if (a->nnumbers > b->nnumbers)
688 return +1;
689
690 if (a->flags < b->flags)
691 return -1;
692 else if (a->flags > b->flags)
693 return +1;
694
695 for (i = 0; i < a->nnumbers; i++) {
696 if (ratcmp(a->numbers+2*i, <, b->numbers+2*i))
697 return -1;
698 else if (ratcmp(a->numbers+2*i, >, b->numbers+2*i))
699 return +1;
700 }
701
702 return 0;
703}
704
705static int outputcmp(void *av, void *bv)
706{
707 struct output *a = (struct output *)av;
708 struct output *b = (struct output *)bv;
709
710 if (a->number < b->number)
711 return -1;
712 else if (a->number > b->number)
713 return +1;
714
715 return 0;
716}
717
718static int outputfindcmp(void *av, void *bv)
719{
720 int *a = (int *)av;
721 struct output *b = (struct output *)bv;
722
723 if (*a < b->number)
724 return -1;
725 else if (*a > b->number)
726 return +1;
727
728 return 0;
729}
730
731static void addset(struct sets *s, struct set *set, int multiple,
732 struct set *prev, int pa, int po, int pb, int pr)
733{
734 struct set *s2;
735 int npaths = (prev ? prev->npaths : 1);
736
737 assert(set == s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN);
738 s2 = add234(s->settree, set);
739 if (s2 == set) {
740 /*
741 * New set added to the tree.
742 */
743 set->a.prev = prev;
744 set->a.pa = pa;
745 set->a.po = po;
746 set->a.pb = pb;
747 set->a.pr = pr;
748 set->npaths = npaths;
749 s->nsets++;
750 s->nnumbers += 2 * set->nnumbers;
751 set->as = NULL;
752 set->nas = set->assize = 0;
753 } else {
754 /*
755 * Rediscovered an existing set. Update its npaths.
756 */
757 s2->npaths += npaths;
758 /*
759 * And optionally enter it as an additional ancestor.
760 */
761 if (multiple) {
762 if (s2->nas >= s2->assize) {
763 s2->assize = s2->nas * 3 / 2 + 4;
764 s2->as = sresize(s2->as, s2->assize, struct ancestor);
765 }
766 s2->as[s2->nas].prev = prev;
767 s2->as[s2->nas].pa = pa;
768 s2->as[s2->nas].po = po;
769 s2->as[s2->nas].pb = pb;
770 s2->as[s2->nas].pr = pr;
771 s2->nas++;
772 }
773 }
774}
775
776static struct set *newset(struct sets *s, int nnumbers, int flags)
777{
778 struct set *sn;
779
780 ensure(s->setlists, s->setlistsize, s->nsets/SETLISTLEN+1, struct set *);
781 while (s->nsetlists <= s->nsets / SETLISTLEN)
782 s->setlists[s->nsetlists++] = snewn(SETLISTLEN, struct set);
783 sn = s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN;
784
785 if (s->nnumbers + nnumbers * 2 > s->nnumberlists * NUMBERLISTLEN)
786 s->nnumbers = s->nnumberlists * NUMBERLISTLEN;
787 ensure(s->numberlists, s->numberlistsize,
788 s->nnumbers/NUMBERLISTLEN+1, int *);
789 while (s->nnumberlists <= s->nnumbers / NUMBERLISTLEN)
790 s->numberlists[s->nnumberlists++] = snewn(NUMBERLISTLEN, int);
791 sn->numbers = s->numberlists[s->nnumbers / NUMBERLISTLEN] +
792 s->nnumbers % NUMBERLISTLEN;
793
794 /*
795 * Start the set off empty.
796 */
797 sn->nnumbers = 0;
798
799 sn->flags = flags;
800
801 return sn;
802}
803
804static int addoutput(struct sets *s, struct set *ss, int index, int *n)
805{
806 struct output *o, *o2;
807
808 /*
809 * Target numbers are always integers.
810 */
811 if (ss->numbers[2*index+1] != 1)
812 return false;
813
814 ensure(s->outputlists, s->outputlistsize, s->noutputs/OUTPUTLISTLEN+1,
815 struct output *);
816 while (s->noutputlists <= s->noutputs / OUTPUTLISTLEN)
817 s->outputlists[s->noutputlists++] = snewn(OUTPUTLISTLEN,
818 struct output);
819 o = s->outputlists[s->noutputs / OUTPUTLISTLEN] +
820 s->noutputs % OUTPUTLISTLEN;
821
822 o->number = ss->numbers[2*index];
823 o->set = ss;
824 o->index = index;
825 o->npaths = ss->npaths;
826 o2 = add234(s->outputtree, o);
827 if (o2 != o) {
828 o2->npaths += o->npaths;
829 } else {
830 s->noutputs++;
831 }
832 *n = o->number;
833 return true;
834}
835
836static struct sets *do_search(int ninputs, int *inputs,
837 const struct rules *rules, int *target,
838 int debug, int multiple)
839{
840 struct sets *s;
841 struct set *sn;
842 int qpos, i;
843 const struct operation *const *ops = rules->ops;
844
845 s = snew(struct sets);
846 s->setlists = NULL;
847 s->nsets = s->nsetlists = s->setlistsize = 0;
848 s->numberlists = NULL;
849 s->nnumbers = s->nnumberlists = s->numberlistsize = 0;
850 s->outputlists = NULL;
851 s->noutputs = s->noutputlists = s->outputlistsize = 0;
852 s->settree = newtree234(setcmp);
853 s->outputtree = newtree234(outputcmp);
854 s->ops = ops;
855
856 /*
857 * Start with the input set.
858 */
859 sn = newset(s, ninputs, SETFLAG_CONCAT);
860 for (i = 0; i < ninputs; i++) {
861 int newnumber[2];
862 newnumber[0] = inputs[i];
863 newnumber[1] = 1;
864 addtoset(sn, newnumber);
865 }
866 addset(s, sn, multiple, NULL, 0, 0, 0, 0);
867
868 /*
869 * Now perform the breadth-first search: keep looping over sets
870 * until we run out of steam.
871 */
872 qpos = 0;
873 while (qpos < s->nsets) {
874 struct set *ss = s->setlists[qpos / SETLISTLEN] + qpos % SETLISTLEN;
875 struct set *sn;
876 int i, j, k, m;
877
878 if (debug) {
879 int i;
880 printf("processing set:");
881 for (i = 0; i < ss->nnumbers; i++) {
882 printf(" %d", ss->numbers[2*i]);
883 if (ss->numbers[2*i+1] != 1)
884 printf("/%d", ss->numbers[2*i+1]);
885 }
886 printf("\n");
887 }
888
889 /*
890 * Record all the valid output numbers in this state. We
891 * can always do this if there's only one number in the
892 * state; otherwise, we can only do it if we aren't
893 * required to use all the numbers in coming to our answer.
894 */
895 if (ss->nnumbers == 1 || !rules->use_all) {
896 for (i = 0; i < ss->nnumbers; i++) {
897 int n;
898
899 if (addoutput(s, ss, i, &n) && target && n == *target)
900 return s;
901 }
902 }
903
904 /*
905 * Try every possible operation from this state.
906 */
907 for (k = 0; ops[k] && ops[k]->perform; k++) {
908 if ((ops[k]->flags & OPFLAG_NEEDS_CONCAT) &&
909 !(ss->flags & SETFLAG_CONCAT))
910 continue; /* can't use this operation here */
911 for (i = 0; i < ss->nnumbers; i++) {
912 int jlimit = (ops[k]->flags & OPFLAG_UNARY ? 1 : ss->nnumbers);
913 for (j = 0; j < jlimit; j++) {
914 int n[2], newnn = ss->nnumbers;
915 int pa, po, pb, pr;
916
917 if (!(ops[k]->flags & OPFLAG_UNARY)) {
918 if (i == j)
919 continue; /* can't combine a number with itself */
920 if (i > j && ops[k]->commutes)
921 continue; /* no need to do this both ways round */
922 newnn--;
923 }
924 if (!ops[k]->perform(ss->numbers+2*i, ss->numbers+2*j, n))
925 continue; /* operation failed */
926
927 sn = newset(s, newnn, ss->flags);
928
929 if (!(ops[k]->flags & OPFLAG_KEEPS_CONCAT))
930 sn->flags &= ~SETFLAG_CONCAT;
931
932 for (m = 0; m < ss->nnumbers; m++) {
933 if (m == i || (!(ops[k]->flags & OPFLAG_UNARY) &&
934 m == j))
935 continue;
936 sn->numbers[2*sn->nnumbers] = ss->numbers[2*m];
937 sn->numbers[2*sn->nnumbers + 1] = ss->numbers[2*m + 1];
938 sn->nnumbers++;
939 }
940 pa = i;
941 if (ops[k]->flags & OPFLAG_UNARY)
942 pb = sn->nnumbers+10;
943 else
944 pb = j;
945 po = k;
946 pr = addtoset(sn, n);
947 addset(s, sn, multiple, ss, pa, po, pb, pr);
948 if (debug) {
949 int i;
950 if (ops[k]->flags & OPFLAG_UNARYPREFIX)
951 printf(" %s %d ->", ops[po]->dbgtext, pa);
952 else if (ops[k]->flags & OPFLAG_UNARY)
953 printf(" %d %s ->", pa, ops[po]->dbgtext);
954 else
955 printf(" %d %s %d ->", pa, ops[po]->dbgtext, pb);
956 for (i = 0; i < sn->nnumbers; i++) {
957 printf(" %d", sn->numbers[2*i]);
958 if (sn->numbers[2*i+1] != 1)
959 printf("/%d", sn->numbers[2*i+1]);
960 }
961 printf("\n");
962 }
963 }
964 }
965 }
966
967 qpos++;
968 }
969
970 return s;
971}
972
973static void free_sets(struct sets *s)
974{
975 int i;
976
977 freetree234(s->settree);
978 freetree234(s->outputtree);
979 for (i = 0; i < s->nsetlists; i++)
980 sfree(s->setlists[i]);
981 sfree(s->setlists);
982 for (i = 0; i < s->nnumberlists; i++)
983 sfree(s->numberlists[i]);
984 sfree(s->numberlists);
985 for (i = 0; i < s->noutputlists; i++)
986 sfree(s->outputlists[i]);
987 sfree(s->outputlists);
988 sfree(s);
989}
990
991/*
992 * Print a text formula for producing a given output.
993 */
994static void print_recurse(struct sets *s, struct set *ss, int pathindex,
995 int index, int priority, int assoc, int child);
996static void print_recurse_inner(struct sets *s, struct set *ss,
997 struct ancestor *a, int pathindex, int index,
998 int priority, int assoc, int child)
999{
1000 if (a->prev && index != a->pr) {
1001 int pi;
1002
1003 /*
1004 * This number was passed straight down from this set's
1005 * predecessor. Find its index in the previous set and
1006 * recurse to there.
1007 */
1008 pi = index;
1009 assert(pi != a->pr);
1010 if (pi > a->pr)
1011 pi--;
1012 if (pi >= min(a->pa, a->pb)) {
1013 pi++;
1014 if (pi >= max(a->pa, a->pb))
1015 pi++;
1016 }
1017 print_recurse(s, a->prev, pathindex, pi, priority, assoc, child);
1018 } else if (a->prev && index == a->pr &&
1019 s->ops[a->po]->display) {
1020 /*
1021 * This number was created by a displayed operator in the
1022 * transition from this set to its predecessor. Hence we
1023 * write an open paren, then recurse into the first
1024 * operand, then write the operator, then the second
1025 * operand, and finally close the paren.
1026 */
1027 const char *op;
1028 int parens, thispri, thisassoc;
1029
1030 /*
1031 * Determine whether we need parentheses.
1032 */
1033 thispri = s->ops[a->po]->priority;
1034 thisassoc = s->ops[a->po]->assoc;
1035 parens = (thispri < priority ||
1036 (thispri == priority && (assoc & child)));
1037
1038 if (parens)
1039 putchar('(');
1040
1041 if (s->ops[a->po]->flags & OPFLAG_UNARYPREFIX)
1042 for (op = s->ops[a->po]->text; *op; op++)
1043 putchar(*op);
1044
1045 if (s->ops[a->po]->flags & OPFLAG_FN)
1046 putchar('(');
1047
1048 print_recurse(s, a->prev, pathindex, a->pa, thispri, thisassoc, 1);
1049
1050 if (s->ops[a->po]->flags & OPFLAG_FN)
1051 putchar(')');
1052
1053 if (!(s->ops[a->po]->flags & OPFLAG_UNARYPREFIX))
1054 for (op = s->ops[a->po]->text; *op; op++)
1055 putchar(*op);
1056
1057 if (!(s->ops[a->po]->flags & OPFLAG_UNARY))
1058 print_recurse(s, a->prev, pathindex, a->pb, thispri, thisassoc, 2);
1059
1060 if (parens)
1061 putchar(')');
1062 } else {
1063 /*
1064 * This number is either an original, or something formed
1065 * by a non-displayed operator (concatenation). Either way,
1066 * we display it as is.
1067 */
1068 printf("%d", ss->numbers[2*index]);
1069 if (ss->numbers[2*index+1] != 1)
1070 printf("/%d", ss->numbers[2*index+1]);
1071 }
1072}
1073static void print_recurse(struct sets *s, struct set *ss, int pathindex,
1074 int index, int priority, int assoc, int child)
1075{
1076 if (!ss->a.prev || pathindex < ss->a.prev->npaths) {
1077 print_recurse_inner(s, ss, &ss->a, pathindex,
1078 index, priority, assoc, child);
1079 } else {
1080 int i;
1081 pathindex -= ss->a.prev->npaths;
1082 for (i = 0; i < ss->nas; i++) {
1083 if (pathindex < ss->as[i].prev->npaths) {
1084 print_recurse_inner(s, ss, &ss->as[i], pathindex,
1085 index, priority, assoc, child);
1086 break;
1087 }
1088 pathindex -= ss->as[i].prev->npaths;
1089 }
1090 }
1091}
1092static void print(int pathindex, struct sets *s, struct output *o)
1093{
1094 print_recurse(s, o->set, pathindex, o->index, 0, 0, 0);
1095}
1096
1097/*
1098 * gcc -g -O0 -o numgame numgame.c -I.. ../{malloc,tree234,nullfe}.c -lm
1099 */
1100int main(int argc, char **argv)
1101{
1102 int doing_opts = true;
1103 const struct rules *rules = NULL;
1104 char *pname = argv[0];
1105 int got_target = false, target = 0;
1106 int numbers[10], nnumbers = 0;
1107 int verbose = false;
1108 int pathcounts = false;
1109 int multiple = false;
1110 int debug_bfs = false;
1111 int got_range = false, rangemin = 0, rangemax = 0;
1112
1113 struct output *o;
1114 struct sets *s;
1115 int i, start, limit;
1116
1117 while (--argc) {
1118 char *p = *++argv;
1119 int c;
1120
1121 if (doing_opts && *p == '-') {
1122 p++;
1123
1124 if (!strcmp(p, "-")) {
1125 doing_opts = false;
1126 continue;
1127 } else if (*p == '-') {
1128 p++;
1129 if (!strcmp(p, "debug-bfs")) {
1130 debug_bfs = true;
1131 } else {
1132 fprintf(stderr, "%s: option '--%s' not recognised\n",
1133 pname, p);
1134 }
1135 } else while (p && *p) switch (c = *p++) {
1136 case 'C':
1137 rules = &rules_countdown;
1138 break;
1139 case 'B':
1140 rules = &rules_3388;
1141 break;
1142 case 'D':
1143 rules = &rules_four4s;
1144 break;
1145 case 'A':
1146 rules = &rules_anythinggoes;
1147 break;
1148 case 'v':
1149 verbose = true;
1150 break;
1151 case 'p':
1152 pathcounts = true;
1153 break;
1154 case 'm':
1155 multiple = true;
1156 break;
1157 case 't':
1158 case 'r':
1159 {
1160 char *v;
1161 if (*p) {
1162 v = p;
1163 p = NULL;
1164 } else if (--argc) {
1165 v = *++argv;
1166 } else {
1167 fprintf(stderr, "%s: option '-%c' expects an"
1168 " argument\n", pname, c);
1169 return 1;
1170 }
1171 switch (c) {
1172 case 't':
1173 got_target = true;
1174 target = atoi(v);
1175 break;
1176 case 'r':
1177 {
1178 char *sep = strchr(v, '-');
1179 got_range = true;
1180 if (sep) {
1181 rangemin = atoi(v);
1182 rangemax = atoi(sep+1);
1183 } else {
1184 rangemin = 0;
1185 rangemax = atoi(v);
1186 }
1187 }
1188 break;
1189 }
1190 }
1191 break;
1192 default:
1193 fprintf(stderr, "%s: option '-%c' not"
1194 " recognised\n", pname, c);
1195 return 1;
1196 }
1197 } else {
1198 if (nnumbers >= lenof(numbers)) {
1199 fprintf(stderr, "%s: internal limit of %d numbers exceeded\n",
1200 pname, (int)lenof(numbers));
1201 return 1;
1202 } else {
1203 numbers[nnumbers++] = atoi(p);
1204 }
1205 }
1206 }
1207
1208 if (!rules) {
1209 fprintf(stderr, "%s: no rule set specified; use -C,-B,-D,-A\n", pname);
1210 return 1;
1211 }
1212
1213 if (!nnumbers) {
1214 fprintf(stderr, "%s: no input numbers specified\n", pname);
1215 return 1;
1216 }
1217
1218 if (got_range) {
1219 if (got_target) {
1220 fprintf(stderr, "%s: only one of -t and -r may be specified\n", pname);
1221 return 1;
1222 }
1223 if (rangemin >= rangemax) {
1224 fprintf(stderr, "%s: range not sensible (%d - %d)\n", pname, rangemin, rangemax);
1225 return 1;
1226 }
1227 }
1228
1229 s = do_search(nnumbers, numbers, rules, (got_target ? &target : NULL),
1230 debug_bfs, multiple);
1231
1232 if (got_target) {
1233 o = findrelpos234(s->outputtree, &target, outputfindcmp,
1234 REL234_LE, &start);
1235 if (!o)
1236 start = -1;
1237 o = findrelpos234(s->outputtree, &target, outputfindcmp,
1238 REL234_GE, &limit);
1239 if (!o)
1240 limit = -1;
1241 assert(start != -1 || limit != -1);
1242 if (start == -1)
1243 start = limit;
1244 else if (limit == -1)
1245 limit = start;
1246 limit++;
1247 } else if (got_range) {
1248 if (!findrelpos234(s->outputtree, &rangemin, outputfindcmp,
1249 REL234_GE, &start) ||
1250 !findrelpos234(s->outputtree, &rangemax, outputfindcmp,
1251 REL234_LE, &limit)) {
1252 printf("No solutions available in specified range %d-%d\n", rangemin, rangemax);
1253 return 1;
1254 }
1255 limit++;
1256 } else {
1257 start = 0;
1258 limit = count234(s->outputtree);
1259 }
1260
1261 for (i = start; i < limit; i++) {
1262 char buf[256];
1263
1264 o = index234(s->outputtree, i);
1265
1266 sprintf(buf, "%d", o->number);
1267
1268 if (pathcounts)
1269 sprintf(buf + strlen(buf), " [%d]", o->npaths);
1270
1271 if (got_target || verbose) {
1272 int j, npaths;
1273
1274 if (multiple)
1275 npaths = o->npaths;
1276 else
1277 npaths = 1;
1278
1279 for (j = 0; j < npaths; j++) {
1280 printf("%s = ", buf);
1281 print(j, s, o);
1282 putchar('\n');
1283 }
1284 } else {
1285 printf("%s\n", buf);
1286 }
1287 }
1288
1289 free_sets(s);
1290
1291 return 0;
1292}
1293
1294/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/unfinished/path.c b/apps/plugins/puzzles/src/unfinished/path.c
new file mode 100644
index 0000000000..2515ed0b71
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/path.c
@@ -0,0 +1,866 @@
1/*
2 * Experimental grid generator for Nikoli's `Number Link' puzzle.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <assert.h>
9#include "puzzles.h"
10
11/*
12 * 2005-07-08: This is currently a Path grid generator which will
13 * construct valid grids at a plausible speed. However, the grids
14 * are not of suitable quality to be used directly as puzzles.
15 *
16 * The basic strategy is to start with an empty grid, and
17 * repeatedly either (a) add a new path to it, or (b) extend one
18 * end of a path by one square in some direction and push other
19 * paths into new shapes in the process. The effect of this is that
20 * we are able to construct a set of paths which between them fill
21 * the entire grid.
22 *
23 * Quality issues: if we set the main loop to do (a) where possible
24 * and (b) only where necessary, we end up with a grid containing a
25 * few too many small paths, which therefore doesn't make for an
26 * interesting puzzle. If we reverse the priority so that we do (b)
27 * where possible and (a) only where necessary, we end up with some
28 * staggeringly interwoven grids with very very few separate paths,
29 * but the result of this is that there's invariably a solution
30 * other than the intended one which leaves many grid squares
31 * unfilled. There's also a separate problem which is that many
32 * grids have really boring and obvious paths in them, such as the
33 * entire bottom row of the grid being taken up by a single path.
34 *
35 * It's not impossible that a few tweaks might eliminate or reduce
36 * the incidence of boring paths, and might also find a happy
37 * medium between too many and too few. There remains the question
38 * of unique solutions, however. I fear there is no alternative but
39 * to write - somehow! - a solver.
40 *
41 * While I'm here, some notes on UI strategy for the parts of the
42 * puzzle implementation that _aren't_ the generator:
43 *
44 * - data model is to track connections between adjacent squares,
45 * so that you aren't limited to extending a path out from each
46 * number but can also mark sections of path which you know
47 * _will_ come in handy later.
48 *
49 * - user interface is to click in one square and drag to an
50 * adjacent one, thus creating a link between them. We can
51 * probably tolerate rapid mouse motion causing a drag directly
52 * to a square which is a rook move away, but any other rapid
53 * motion is ambiguous and probably the best option is to wait
54 * until the mouse returns to a square we know how to reach.
55 *
56 * - a drag causing the current path to backtrack has the effect
57 * of removing bits of it.
58 *
59 * - the UI should enforce at all times the constraint that at
60 * most two links can come into any square.
61 *
62 * - my Cunning Plan for actually implementing this: the game_ui
63 * contains a grid-sized array, which is copied from the current
64 * game_state on starting a drag. While a drag is active, the
65 * contents of the game_ui is adjusted with every mouse motion,
66 * and is displayed _in place_ of the game_state itself. On
67 * termination of a drag, the game_ui array is copied back into
68 * the new game_state (or rather, a string move is encoded which
69 * has precisely the set of link changes to cause that effect).
70 */
71
72/*
73 * 2020-05-11: some thoughts on a solver.
74 *
75 * Consider this example puzzle, from Wikipedia:
76 *
77 * ---4---
78 * -3--25-
79 * ---31--
80 * ---5---
81 * -------
82 * --1----
83 * 2---4--
84 *
85 * The kind of deduction that a human wants to make here is: which way
86 * does the path between the 4s go? In particular, does it go round
87 * the left of the W-shaped cluster of endpoints, or round the right
88 * of it? It's clear at a glance that it must go to the right, because
89 * _any_ path between the 4s that goes to the left of that cluster, no
90 * matter what detailed direction it takes, will disconnect the
91 * remaining grid squares into two components, with the two 2s not in
92 * the same component. So we immediately know that the path between
93 * the 4s _must_ go round the right-hand side of the grid.
94 *
95 * How do you model that global and topological reasoning in a
96 * computer?
97 *
98 * The most plausible idea I've seen so far is to use fundamental
99 * groups. The fundamental group of loops based at a given point in a
100 * space is a free group, under loop concatenation and up to homotopy,
101 * generated by the loops that go in each direction around each hole
102 * in the space. In this case, the 'holes' are clues, or connected
103 * groups of clues.
104 *
105 * So you might be able to enumerate all the homotopy classes of paths
106 * between (say) the two 4s as follows. Start with any old path
107 * between them (say, find the first one that breadth-first search
108 * will give you). Choose one of the 4s to regard as the base point
109 * (arbitrarily). Then breadth-first search among the space of _paths_
110 * by the following procedure. Given a candidate path, append to it
111 * each of the possible loops that starts from the base point,
112 * circumnavigates one clue cluster, and returns to the base point.
113 * The result will typically be a path that retraces its steps and
114 * self-intersects. Now adjust it homotopically so that it doesn't. If
115 * that can't be done, then we haven't generated a fresh candidate
116 * path; if it can, then we've got a new path that is not homotopic to
117 * any path we already had, so add it to our list and queue it up to
118 * become the starting point of this search later.
119 *
120 * The idea is that this should exhaustively enumerate, up to
121 * homotopy, the different ways in which the two 4s can connect to
122 * each other within the constraint that you have to actually fit the
123 * path non-self-intersectingly into this grid. Then you can keep a
124 * list of those homotopy classes in mind, and start ruling them out
125 * by techniques like the connectivity approach described above.
126 * Hopefully you end up narrowing down to few enough homotopy classes
127 * that you can deduce something concrete about actual squares of the
128 * grid - for example, here, that if the path between 4s has to go
129 * round the right, then we know some specific squares it must go
130 * through, so we can fill those in. And then, having filled in a
131 * piece of the middle of a path, you can now regard connecting the
132 * ultimate endpoints to that mid-section as two separate subproblems,
133 * so you've reduced to a simpler instance of the same puzzle.
134 *
135 * But I don't know whether all of this actually works. I more or less
136 * believe the process for enumerating elements of the free group; but
137 * I'm not as confident that when you find a group element that won't
138 * fit in the grid, you'll never have to consider its descendants in
139 * the BFS either. And I'm assuming that 'unwind the self-intersection
140 * homotopically' is a thing that can actually be turned into a
141 * sensible algorithm.
142 *
143 * --------
144 *
145 * Another thing that might be needed is to characterise _which_
146 * homotopy class a given path is in.
147 *
148 * For this I think it's sufficient to choose a collection of paths
149 * along the _edges_ of the square grid, each of which connects two of
150 * the holes in the grid (including the grid exterior, which counts as
151 * a huge hole), such that they form a spanning tree between the
152 * holes. Then assign each of those paths an orientation, so that
153 * crossing it in one direction counts as 'positive' and the other
154 * 'negative'. Now analyse a candidate path from one square to another
155 * by following it and noting down which of those paths it crosses in
156 * which direction, then simplifying the result like a free group word
157 * (i.e. adjacent + and - crossings of the same path cancel out).
158 *
159 * --------
160 *
161 * If we choose those paths to be of minimal length, then we can get
162 * an upper bound on the number of homotopy classes by observing that
163 * you can't traverse any of those barriers more times than will fit
164 * non-self-intersectingly in the grid. That might be an alternative
165 * method of bounding the search through the fundamental group to only
166 * finitely many possibilities.
167 */
168
169/*
170 * Standard notation for directions.
171 */
172#define L 0
173#define U 1
174#define R 2
175#define D 3
176#define DX(dir) ( (dir)==L ? -1 : (dir)==R ? +1 : 0)
177#define DY(dir) ( (dir)==U ? -1 : (dir)==D ? +1 : 0)
178
179/*
180 * Perform a breadth-first search over a grid of squares with the
181 * colour of square (X,Y) given by grid[Y*w+X]. The search begins
182 * at (x,y), and finds all squares which are the same colour as
183 * (x,y) and reachable from it by orthogonal moves. On return:
184 * - dist[Y*w+X] gives the distance of (X,Y) from (x,y), or -1 if
185 * unreachable or a different colour
186 * - the returned value is the number of reachable squares,
187 * including (x,y) itself
188 * - list[0] up to list[returned value - 1] list those squares, in
189 * increasing order of distance from (x,y) (and in arbitrary
190 * order within that).
191 */
192static int bfs(int w, int h, int *grid, int x, int y, int *dist, int *list)
193{
194 int i, j, c, listsize, listdone;
195
196 /*
197 * Start by clearing the output arrays.
198 */
199 for (i = 0; i < w*h; i++)
200 dist[i] = list[i] = -1;
201
202 /*
203 * Set up the initial list.
204 */
205 listsize = 1;
206 listdone = 0;
207 list[0] = y*w+x;
208 dist[y*w+x] = 0;
209 c = grid[y*w+x];
210
211 /*
212 * Repeatedly process a square and add any extra squares to the
213 * end of list.
214 */
215 while (listdone < listsize) {
216 i = list[listdone++];
217 y = i / w;
218 x = i % w;
219 for (j = 0; j < 4; j++) {
220 int xx, yy, ii;
221
222 xx = x + DX(j);
223 yy = y + DY(j);
224 ii = yy*w+xx;
225
226 if (xx >= 0 && xx < w && yy >= 0 && yy < h &&
227 grid[ii] == c && dist[ii] == -1) {
228 dist[ii] = dist[i] + 1;
229 assert(listsize < w*h);
230 list[listsize++] = ii;
231 }
232 }
233 }
234
235 return listsize;
236}
237
238struct genctx {
239 int w, h;
240 int *grid, *sparegrid, *sparegrid2, *sparegrid3;
241 int *dist, *list;
242
243 int npaths, pathsize;
244 int *pathends, *sparepathends; /* 2*npaths entries */
245 int *pathspare; /* npaths entries */
246 int *extends; /* 8*npaths entries */
247};
248
249static struct genctx *new_genctx(int w, int h)
250{
251 struct genctx *ctx = snew(struct genctx);
252 ctx->w = w;
253 ctx->h = h;
254 ctx->grid = snewn(w * h, int);
255 ctx->sparegrid = snewn(w * h, int);
256 ctx->sparegrid2 = snewn(w * h, int);
257 ctx->sparegrid3 = snewn(w * h, int);
258 ctx->dist = snewn(w * h, int);
259 ctx->list = snewn(w * h, int);
260 ctx->npaths = ctx->pathsize = 0;
261 ctx->pathends = ctx->sparepathends = ctx->pathspare = ctx->extends = NULL;
262 return ctx;
263}
264
265static void free_genctx(struct genctx *ctx)
266{
267 sfree(ctx->grid);
268 sfree(ctx->sparegrid);
269 sfree(ctx->sparegrid2);
270 sfree(ctx->sparegrid3);
271 sfree(ctx->dist);
272 sfree(ctx->list);
273 sfree(ctx->pathends);
274 sfree(ctx->sparepathends);
275 sfree(ctx->pathspare);
276 sfree(ctx->extends);
277}
278
279static int newpath(struct genctx *ctx)
280{
281 int n;
282
283 n = ctx->npaths++;
284 if (ctx->npaths > ctx->pathsize) {
285 ctx->pathsize += 16;
286 ctx->pathends = sresize(ctx->pathends, ctx->pathsize*2, int);
287 ctx->sparepathends = sresize(ctx->sparepathends, ctx->pathsize*2, int);
288 ctx->pathspare = sresize(ctx->pathspare, ctx->pathsize, int);
289 ctx->extends = sresize(ctx->extends, ctx->pathsize*8, int);
290 }
291 return n;
292}
293
294static int is_endpoint(struct genctx *ctx, int x, int y)
295{
296 int w = ctx->w, h = ctx->h, c;
297
298 assert(x >= 0 && x < w && y >= 0 && y < h);
299
300 c = ctx->grid[y*w+x];
301 if (c < 0)
302 return false; /* empty square is not an endpoint! */
303 assert(c >= 0 && c < ctx->npaths);
304 if (ctx->pathends[c*2] == y*w+x || ctx->pathends[c*2+1] == y*w+x)
305 return true;
306 return false;
307}
308
309/*
310 * Tries to extend a path by one square in the given direction,
311 * pushing other paths around if necessary. Returns true on success
312 * or false on failure.
313 */
314static int extend_path(struct genctx *ctx, int path, int end, int direction)
315{
316 int w = ctx->w, h = ctx->h;
317 int x, y, xe, ye, cut;
318 int i, j, jp, n, first, last;
319
320 assert(path >= 0 && path < ctx->npaths);
321 assert(end == 0 || end == 1);
322
323 /*
324 * Find the endpoint of the path and the point we plan to
325 * extend it into.
326 */
327 y = ctx->pathends[path * 2 + end] / w;
328 x = ctx->pathends[path * 2 + end] % w;
329 assert(x >= 0 && x < w && y >= 0 && y < h);
330
331 xe = x + DX(direction);
332 ye = y + DY(direction);
333 if (xe < 0 || xe >= w || ye < 0 || ye >= h)
334 return false; /* could not extend in this direction */
335
336 /*
337 * We don't extend paths _directly_ into endpoints of other
338 * paths, although we don't mind too much if a knock-on effect
339 * of an extension is to push part of another path into a third
340 * path's endpoint.
341 */
342 if (is_endpoint(ctx, xe, ye))
343 return false;
344
345 /*
346 * We can't extend a path back the way it came.
347 */
348 if (ctx->grid[ye*w+xe] == path)
349 return false;
350
351 /*
352 * Paths may not double back on themselves. Check if the new
353 * point is adjacent to any point of this path other than (x,y).
354 */
355 for (j = 0; j < 4; j++) {
356 int xf, yf;
357
358 xf = xe + DX(j);
359 yf = ye + DY(j);
360
361 if (xf >= 0 && xf < w && yf >= 0 && yf < h &&
362 (xf != x || yf != y) && ctx->grid[yf*w+xf] == path)
363 return false;
364 }
365
366 /*
367 * Now we're convinced it's valid to _attempt_ the extension.
368 * It may still fail if we run out of space to push other paths
369 * into.
370 *
371 * So now we can set up our temporary data structures. We will
372 * need:
373 *
374 * - a spare copy of the grid on which to gradually move paths
375 * around (sparegrid)
376 *
377 * - a second spare copy with which to remember how paths
378 * looked just before being cut (sparegrid2). FIXME: is
379 * sparegrid2 necessary? right now it's never different from
380 * grid itself
381 *
382 * - a third spare copy with which to do the internal
383 * calculations involved in reconstituting a cut path
384 * (sparegrid3)
385 *
386 * - something to track which paths currently need
387 * reconstituting after being cut, and which have already
388 * been cut (pathspare)
389 *
390 * - a spare copy of pathends to store the altered states in
391 * (sparepathends)
392 */
393 memcpy(ctx->sparegrid, ctx->grid, w*h*sizeof(int));
394 memcpy(ctx->sparegrid2, ctx->grid, w*h*sizeof(int));
395 memcpy(ctx->sparepathends, ctx->pathends, ctx->npaths*2*sizeof(int));
396 for (i = 0; i < ctx->npaths; i++)
397 ctx->pathspare[i] = 0; /* 0=untouched, 1=broken, 2=fixed */
398
399 /*
400 * Working in sparegrid, actually extend the path. If it cuts
401 * another, begin a loop in which we restore any cut path by
402 * moving it out of the way.
403 */
404 cut = ctx->sparegrid[ye*w+xe];
405 ctx->sparegrid[ye*w+xe] = path;
406 ctx->sparepathends[path*2+end] = ye*w+xe;
407 ctx->pathspare[path] = 2; /* this one is sacrosanct */
408 if (cut >= 0) {
409 assert(cut >= 0 && cut < ctx->npaths);
410 ctx->pathspare[cut] = 1; /* broken */
411
412 while (1) {
413 for (i = 0; i < ctx->npaths; i++)
414 if (ctx->pathspare[i] == 1)
415 break;
416 if (i == ctx->npaths)
417 break; /* we're done */
418
419 /*
420 * Path i needs restoring. So walk along its original
421 * track (as given in sparegrid2) and see where it's
422 * been cut. Where it has, surround the cut points in
423 * the same colour, without overwriting already-fixed
424 * paths.
425 */
426 memcpy(ctx->sparegrid3, ctx->sparegrid, w*h*sizeof(int));
427 n = bfs(w, h, ctx->sparegrid2,
428 ctx->pathends[i*2] % w, ctx->pathends[i*2] / w,
429 ctx->dist, ctx->list);
430 first = last = -1;
431if (ctx->sparegrid3[ctx->pathends[i*2]] != i ||
432 ctx->sparegrid3[ctx->pathends[i*2+1]] != i) return false;/* FIXME */
433 for (j = 0; j < n; j++) {
434 jp = ctx->list[j];
435 assert(ctx->dist[jp] == j);
436 assert(ctx->sparegrid2[jp] == i);
437
438 /*
439 * Wipe out the original path in sparegrid.
440 */
441 if (ctx->sparegrid[jp] == i)
442 ctx->sparegrid[jp] = -1;
443
444 /*
445 * Be prepared to shorten the path at either end if
446 * the endpoints have been stomped on.
447 */
448 if (ctx->sparegrid3[jp] == i) {
449 if (first < 0)
450 first = jp;
451 last = jp;
452 }
453
454 if (ctx->sparegrid3[jp] != i) {
455 int jx = jp % w, jy = jp / w;
456 int dx, dy;
457 for (dy = -1; dy <= +1; dy++)
458 for (dx = -1; dx <= +1; dx++) {
459 int newp, newv;
460 if (!dy && !dx)
461 continue; /* central square */
462 if (jx+dx < 0 || jx+dx >= w ||
463 jy+dy < 0 || jy+dy >= h)
464 continue; /* out of range */
465 newp = (jy+dy)*w+(jx+dx);
466 newv = ctx->sparegrid3[newp];
467 if (newv >= 0 && (newv == i ||
468 ctx->pathspare[newv] == 2))
469 continue; /* can't use this square */
470 ctx->sparegrid3[newp] = i;
471 }
472 }
473 }
474
475 if (first < 0 || last < 0)
476 return false; /* path is completely wiped out! */
477
478 /*
479 * Now we've covered sparegrid3 in possible squares for
480 * the new layout of path i. Find the actual layout
481 * we're going to use by bfs: we want the shortest path
482 * from one endpoint to the other.
483 */
484 n = bfs(w, h, ctx->sparegrid3, first % w, first / w,
485 ctx->dist, ctx->list);
486 if (ctx->dist[last] < 2) {
487 /*
488 * Either there is no way to get between the path's
489 * endpoints, or the remaining endpoints simply
490 * aren't far enough apart to make the path viable
491 * any more. This means the entire push operation
492 * has failed.
493 */
494 return false;
495 }
496
497 /*
498 * Write the new path into sparegrid. Also save the new
499 * endpoint locations, in case they've changed.
500 */
501 jp = last;
502 j = ctx->dist[jp];
503 while (1) {
504 int d;
505
506 if (ctx->sparegrid[jp] >= 0) {
507 if (ctx->pathspare[ctx->sparegrid[jp]] == 2)
508 return false; /* somehow we've hit a fixed path */
509 ctx->pathspare[ctx->sparegrid[jp]] = 1; /* broken */
510 }
511 ctx->sparegrid[jp] = i;
512
513 if (j == 0)
514 break;
515
516 /*
517 * Now look at the neighbours of jp to find one
518 * which has dist[] one less.
519 */
520 for (d = 0; d < 4; d++) {
521 int jx = (jp % w) + DX(d), jy = (jp / w) + DY(d);
522 if (jx >= 0 && jx < w && jy >= 0 && jy < w &&
523 ctx->dist[jy*w+jx] == j-1) {
524 jp = jy*w+jx;
525 j--;
526 break;
527 }
528 }
529 assert(d < 4);
530 }
531
532 ctx->sparepathends[i*2] = first;
533 ctx->sparepathends[i*2+1] = last;
534/* printf("new ends of path %d: %d,%d\n", i, first, last); */
535 ctx->pathspare[i] = 2; /* fixed */
536 }
537 }
538
539 /*
540 * If we got here, the extension was successful!
541 */
542 memcpy(ctx->grid, ctx->sparegrid, w*h*sizeof(int));
543 memcpy(ctx->pathends, ctx->sparepathends, ctx->npaths*2*sizeof(int));
544 return true;
545}
546
547/*
548 * Tries to add a new path to the grid.
549 */
550static int add_path(struct genctx *ctx, random_state *rs)
551{
552 int w = ctx->w, h = ctx->h;
553 int i, ii, n;
554
555 /*
556 * Our strategy is:
557 * - randomly choose an empty square in the grid
558 * - do a BFS from that point to find a long path starting
559 * from it
560 * - if we run out of viable empty squares, return failure.
561 */
562
563 /*
564 * Use `sparegrid' to collect a list of empty squares.
565 */
566 n = 0;
567 for (i = 0; i < w*h; i++)
568 if (ctx->grid[i] == -1)
569 ctx->sparegrid[n++] = i;
570
571 /*
572 * Shuffle the grid.
573 */
574 for (i = n; i-- > 1 ;) {
575 int k = random_upto(rs, i+1);
576 if (k != i) {
577 int t = ctx->sparegrid[i];
578 ctx->sparegrid[i] = ctx->sparegrid[k];
579 ctx->sparegrid[k] = t;
580 }
581 }
582
583 /*
584 * Loop over it trying to add paths. This looks like a
585 * horrifying N^4 algorithm (that is, (w*h)^2), but I predict
586 * that in fact the worst case will very rarely arise because
587 * when there's lots of grid space an attempt will succeed very
588 * quickly.
589 */
590 for (ii = 0; ii < n; ii++) {
591 int i = ctx->sparegrid[ii];
592 int y = i / w, x = i % w, nsq;
593 int r, c, j;
594
595 /*
596 * BFS from here to find long paths.
597 */
598 nsq = bfs(w, h, ctx->grid, x, y, ctx->dist, ctx->list);
599
600 /*
601 * If there aren't any long enough, give up immediately.
602 */
603 assert(nsq > 0); /* must be the start square at least! */
604 if (ctx->dist[ctx->list[nsq-1]] < 3)
605 continue;
606
607 /*
608 * Find the first viable endpoint in ctx->list (i.e. the
609 * first point with distance at least three). I could
610 * binary-search for this, but that would be O(log N)
611 * whereas in fact I can get a constant time bound by just
612 * searching up from the start - after all, there can be at
613 * most 13 points at _less_ than distance 3 from the
614 * starting one!
615 */
616 for (j = 0; j < nsq; j++)
617 if (ctx->dist[ctx->list[j]] >= 3)
618 break;
619 assert(j < nsq); /* we tested above that there was one */
620
621 /*
622 * Now we know that any element of `list' between j and nsq
623 * would be valid in principle. However, we want a few long
624 * paths rather than many small ones, so select only those
625 * elements which are either the maximum length or one
626 * below it.
627 */
628 while (ctx->dist[ctx->list[j]] + 1 < ctx->dist[ctx->list[nsq-1]])
629 j++;
630 r = j + random_upto(rs, nsq - j);
631 j = ctx->list[r];
632
633 /*
634 * And that's our endpoint. Mark the new path on the grid.
635 */
636 c = newpath(ctx);
637 ctx->pathends[c*2] = i;
638 ctx->pathends[c*2+1] = j;
639 ctx->grid[j] = c;
640 while (j != i) {
641 int d, np, index, pts[4];
642 np = 0;
643 for (d = 0; d < 4; d++) {
644 int xn = (j % w) + DX(d), yn = (j / w) + DY(d);
645 if (xn >= 0 && xn < w && yn >= 0 && yn < w &&
646 ctx->dist[yn*w+xn] == ctx->dist[j] - 1)
647 pts[np++] = yn*w+xn;
648 }
649 if (np > 1)
650 index = random_upto(rs, np);
651 else
652 index = 0;
653 j = pts[index];
654 ctx->grid[j] = c;
655 }
656
657 return true;
658 }
659
660 return false;
661}
662
663/*
664 * The main grid generation loop.
665 */
666static void gridgen_mainloop(struct genctx *ctx, random_state *rs)
667{
668 int w = ctx->w, h = ctx->h;
669 int i, n;
670
671 /*
672 * The generation algorithm doesn't always converge. Loop round
673 * until it does.
674 */
675 while (1) {
676 for (i = 0; i < w*h; i++)
677 ctx->grid[i] = -1;
678 ctx->npaths = 0;
679
680 while (1) {
681 /*
682 * See if the grid is full.
683 */
684 for (i = 0; i < w*h; i++)
685 if (ctx->grid[i] < 0)
686 break;
687 if (i == w*h)
688 return;
689
690#ifdef GENERATION_DIAGNOSTICS
691 {
692 int x, y;
693 for (y = 0; y < h; y++) {
694 printf("|");
695 for (x = 0; x < w; x++) {
696 if (ctx->grid[y*w+x] >= 0)
697 printf("%2d", ctx->grid[y*w+x]);
698 else
699 printf(" .");
700 }
701 printf(" |\n");
702 }
703 }
704#endif
705 /*
706 * Try adding a path.
707 */
708 if (add_path(ctx, rs)) {
709#ifdef GENERATION_DIAGNOSTICS
710 printf("added path\n");
711#endif
712 continue;
713 }
714
715 /*
716 * Try extending a path. First list all the possible
717 * extensions.
718 */
719 for (i = 0; i < ctx->npaths * 8; i++)
720 ctx->extends[i] = i;
721 n = i;
722
723 /*
724 * Then shuffle the list.
725 */
726 for (i = n; i-- > 1 ;) {
727 int k = random_upto(rs, i+1);
728 if (k != i) {
729 int t = ctx->extends[i];
730 ctx->extends[i] = ctx->extends[k];
731 ctx->extends[k] = t;
732 }
733 }
734
735 /*
736 * Now try each one in turn until one works.
737 */
738 for (i = 0; i < n; i++) {
739 int p, d, e;
740 p = ctx->extends[i];
741 d = p % 4;
742 p /= 4;
743 e = p % 2;
744 p /= 2;
745
746#ifdef GENERATION_DIAGNOSTICS
747 printf("trying to extend path %d end %d (%d,%d) in dir %d\n", p, e,
748 ctx->pathends[p*2+e] % w,
749 ctx->pathends[p*2+e] / w, d);
750#endif
751 if (extend_path(ctx, p, e, d)) {
752#ifdef GENERATION_DIAGNOSTICS
753 printf("extended path %d end %d (%d,%d) in dir %d\n", p, e,
754 ctx->pathends[p*2+e] % w,
755 ctx->pathends[p*2+e] / w, d);
756#endif
757 break;
758 }
759 }
760
761 if (i < n)
762 continue;
763
764 break;
765 }
766 }
767}
768
769/*
770 * Wrapper function which deals with the boring bits such as
771 * removing the solution from the generated grid, shuffling the
772 * numeric labels and creating/disposing of the context structure.
773 */
774static int *gridgen(int w, int h, random_state *rs)
775{
776 struct genctx *ctx;
777 int *ret;
778 int i;
779
780 ctx = new_genctx(w, h);
781
782 gridgen_mainloop(ctx, rs);
783
784 /*
785 * There is likely to be an ordering bias in the numbers
786 * (longer paths on lower numbers due to there having been more
787 * grid space when laying them down). So we must shuffle the
788 * numbers. We use ctx->pathspare for this.
789 *
790 * This is also as good a time as any to shift to numbering
791 * from 1, for display to the user.
792 */
793 for (i = 0; i < ctx->npaths; i++)
794 ctx->pathspare[i] = i+1;
795 for (i = ctx->npaths; i-- > 1 ;) {
796 int k = random_upto(rs, i+1);
797 if (k != i) {
798 int t = ctx->pathspare[i];
799 ctx->pathspare[i] = ctx->pathspare[k];
800 ctx->pathspare[k] = t;
801 }
802 }
803
804 /* FIXME: remove this at some point! */
805 {
806 int y, x;
807 for (y = 0; y < h; y++) {
808 printf("|");
809 for (x = 0; x < w; x++) {
810 assert(ctx->grid[y*w+x] >= 0);
811 printf("%2d", ctx->pathspare[ctx->grid[y*w+x]]);
812 }
813 printf(" |\n");
814 }
815 printf("\n");
816 }
817
818 /*
819 * Clear the grid, and write in just the endpoints.
820 */
821 for (i = 0; i < w*h; i++)
822 ctx->grid[i] = 0;
823 for (i = 0; i < ctx->npaths; i++) {
824 ctx->grid[ctx->pathends[i*2]] =
825 ctx->grid[ctx->pathends[i*2+1]] = ctx->pathspare[i];
826 }
827
828 ret = ctx->grid;
829 ctx->grid = NULL;
830
831 free_genctx(ctx);
832
833 return ret;
834}
835
836#ifdef TEST_GEN
837
838#define TEST_GENERAL
839
840int main(void)
841{
842 int w = 10, h = 8;
843 random_state *rs = random_new("12345", 5);
844 int x, y, i, *grid;
845
846 for (i = 0; i < 10; i++) {
847 grid = gridgen(w, h, rs);
848
849 for (y = 0; y < h; y++) {
850 printf("|");
851 for (x = 0; x < w; x++) {
852 if (grid[y*w+x] > 0)
853 printf("%2d", grid[y*w+x]);
854 else
855 printf(" .");
856 }
857 printf(" |\n");
858 }
859 printf("\n");
860
861 sfree(grid);
862 }
863
864 return 0;
865}
866#endif
diff --git a/apps/plugins/puzzles/src/unfinished/separate.c b/apps/plugins/puzzles/src/unfinished/separate.c
new file mode 100644
index 0000000000..6ca07252ad
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/separate.c
@@ -0,0 +1,861 @@
1/*
2 * separate.c: Implementation of `Block Puzzle', a Japanese-only
3 * Nikoli puzzle seen at
4 * http://www.nikoli.co.jp/ja/puzzles/block_puzzle/
5 *
6 * It's difficult to be absolutely sure of the rules since online
7 * Japanese translators are so bad, but looking at the sample
8 * puzzle it seems fairly clear that the rules of this one are
9 * very simple. You have an mxn grid in which every square
10 * contains a letter, there are k distinct letters with k dividing
11 * mn, and every letter occurs the same number of times; your aim
12 * is to find a partition of the grid into disjoint k-ominoes such
13 * that each k-omino contains exactly one of each letter.
14 *
15 * (It may be that Nikoli always have m,n,k equal to one another.
16 * However, I don't see that that's critical to the puzzle; k|mn
17 * is the only really important constraint, and even that could
18 * probably be dispensed with if some squares were marked as
19 * unused.)
20 */
21
22/*
23 * Current status: only the solver/generator is yet written, and
24 * although working in principle it's _very_ slow. It generates
25 * 5x5n5 or 6x6n4 readily enough, 6x6n6 with a bit of effort, and
26 * 7x7n7 only with a serious strain. I haven't dared try it higher
27 * than that yet.
28 *
29 * One idea to speed it up is to implement more of the solver.
30 * Ideas I've so far had include:
31 *
32 * - Generalise the deduction currently expressed as `an
33 * undersized chain with only one direction to extend must take
34 * it'. More generally, the deduction should say `if all the
35 * possible k-ominoes containing a given chain also contain
36 * square x, then mark square x as part of that k-omino'.
37 * + For example, consider this case:
38 *
39 * a ? b This represents the top left of a board; the letters
40 * ? ? ? a,b,c do not represent the letters used in the puzzle,
41 * c ? ? but indicate that those three squares are known to be
42 * of different ominoes. Now if k >= 4, we can immediately
43 * deduce that the square midway between b and c belongs to the
44 * same omino as a, because there is no way we can make a 4-or-
45 * more-omino containing a which does not also contain that square.
46 * (Most easily seen by imagining cutting that square out of the
47 * grid; then, clearly, the omino containing a has only two
48 * squares to expand into, and needs at least three.)
49 *
50 * The key difficulty with this mode of reasoning is
51 * identifying such squares. I can't immediately think of a
52 * simple algorithm for finding them on a wholesale basis.
53 *
54 * - Bfs out from a chain looking for the letters it lacks. For
55 * example, in this situation (top three rows of a 7x7n7 grid):
56 *
57 * +-----------+-+
58 * |E-A-F-B-C D|D|
59 * +------- ||
60 * |E-C-G-D G|G E|
61 * +-+--- |
62 * |E|E G A B F A|
63 *
64 * In this situation we can be sure that the top left chain
65 * E-A-F-B-C does extend rightwards to the D, because there is
66 * no other D within reach of that chain. Note also that the
67 * bfs can skip squares which are known to belong to other
68 * ominoes than this one.
69 *
70 * (This deduction, I fear, should only be used in an
71 * emergency, because it relies on _all_ squares within range
72 * of the bfs having particular values and so using it during
73 * incremental generation rather nails down a lot of the grid.)
74 *
75 * It's conceivable that another thing we could do would be to
76 * increase the flexibility in the grid generator: instead of
77 * nailing down the _value_ of any square depended on, merely nail
78 * down its equivalence to other squares. Unfortunately this turns
79 * the letter-selection phase of generation into a general graph
80 * colouring problem (we must draw a graph with equivalence
81 * classes of squares as the vertices, and an edge between any two
82 * vertices representing equivalence classes which contain squares
83 * that share an omino, and then k-colour the result) and hence
84 * requires recursion, which bodes ill for something we're doing
85 * that many times per generation.
86 *
87 * I suppose a simple thing I could try would be tuning the retry
88 * count, just in case it's set too high or too low for efficient
89 * generation.
90 */
91
92#include <stdio.h>
93#include <stdlib.h>
94#include <string.h>
95#include <assert.h>
96#include <ctype.h>
97#ifdef NO_TGMATH_H
98# include <math.h>
99#else
100# include <tgmath.h>
101#endif
102
103#include "puzzles.h"
104
105enum {
106 COL_BACKGROUND,
107 NCOLOURS
108};
109
110struct game_params {
111 int w, h, k;
112};
113
114struct game_state {
115 int FIXME;
116};
117
118static game_params *default_params(void)
119{
120 game_params *ret = snew(game_params);
121
122 ret->w = ret->h = ret->k = 5; /* FIXME: a bit bigger? */
123
124 return ret;
125}
126
127static bool game_fetch_preset(int i, char **name, game_params **params)
128{
129 return false;
130}
131
132static void free_params(game_params *params)
133{
134 sfree(params);
135}
136
137static game_params *dup_params(const game_params *params)
138{
139 game_params *ret = snew(game_params);
140 *ret = *params; /* structure copy */
141 return ret;
142}
143
144static void decode_params(game_params *params, char const *string)
145{
146 params->w = params->h = params->k = atoi(string);
147 while (*string && isdigit((unsigned char)*string)) string++;
148 if (*string == 'x') {
149 string++;
150 params->h = atoi(string);
151 while (*string && isdigit((unsigned char)*string)) string++;
152 }
153 if (*string == 'n') {
154 string++;
155 params->k = atoi(string);
156 while (*string && isdigit((unsigned char)*string)) string++;
157 }
158}
159
160static char *encode_params(const game_params *params, bool full)
161{
162 char buf[256];
163 sprintf(buf, "%dx%dn%d", params->w, params->h, params->k);
164 return dupstr(buf);
165}
166
167static config_item *game_configure(const game_params *params)
168{
169 return NULL;
170}
171
172static game_params *custom_params(const config_item *cfg)
173{
174 return NULL;
175}
176
177static const char *validate_params(const game_params *params, bool full)
178{
179 return NULL;
180}
181
182/* ----------------------------------------------------------------------
183 * Solver and generator.
184 */
185
186struct solver_scratch {
187 int w, h, k;
188
189 /*
190 * Tracks connectedness between squares.
191 */
192 DSF *dsf;
193
194 /*
195 * size[dsf_canonify(dsf, yx)] tracks the size of the
196 * connected component containing yx.
197 */
198 int *size;
199
200 /*
201 * contents[dsf_canonify(dsf, yx)*k+i] tracks whether or not
202 * the connected component containing yx includes letter i. If
203 * the value is -1, it doesn't; otherwise its value is the
204 * index in the main grid of the square which contributes that
205 * letter to the component.
206 */
207 int *contents;
208
209 /*
210 * disconnect[dsf_canonify(dsf, yx1)*w*h + dsf_canonify(dsf, yx2)]
211 * tracks whether or not the connected components containing
212 * yx1 and yx2 are known to be distinct.
213 */
214 bool *disconnect;
215
216 /*
217 * Temporary space used only inside particular solver loops.
218 */
219 int *tmp;
220};
221
222static struct solver_scratch *solver_scratch_new(int w, int h, int k)
223{
224 int wh = w*h;
225 struct solver_scratch *sc = snew(struct solver_scratch);
226
227 sc->w = w;
228 sc->h = h;
229 sc->k = k;
230
231 sc->dsf = dsf_new(wh);
232 sc->size = snewn(wh, int);
233 sc->contents = snewn(wh * k, int);
234 sc->disconnect = snewn(wh*wh, bool);
235 sc->tmp = snewn(wh, int);
236
237 return sc;
238}
239
240static void solver_scratch_free(struct solver_scratch *sc)
241{
242 dsf_free(sc->dsf);
243 sfree(sc->size);
244 sfree(sc->contents);
245 sfree(sc->disconnect);
246 sfree(sc->tmp);
247 sfree(sc);
248}
249
250static void solver_connect(struct solver_scratch *sc, int yx1, int yx2)
251{
252 int w = sc->w, h = sc->h, k = sc->k;
253 int wh = w*h;
254 int i, yxnew;
255
256 yx1 = dsf_canonify(sc->dsf, yx1);
257 yx2 = dsf_canonify(sc->dsf, yx2);
258 assert(yx1 != yx2);
259
260 /*
261 * To connect two components together into a bigger one, we
262 * start by merging them in the dsf itself.
263 */
264 dsf_merge(sc->dsf, yx1, yx2);
265 yxnew = dsf_canonify(sc->dsf, yx2);
266
267 /*
268 * The size of the new component is the sum of the sizes of the
269 * old ones.
270 */
271 sc->size[yxnew] = sc->size[yx1] + sc->size[yx2];
272
273 /*
274 * The contents bitmap of the new component is the union of the
275 * contents of the old ones.
276 *
277 * Given two numbers at most one of which is not -1, we can
278 * find the other one by adding the two and adding 1; this
279 * will yield -1 if both were -1 to begin with, otherwise the
280 * other.
281 *
282 * (A neater approach would be to take their bitwise AND, but
283 * this is unfortunately not well-defined standard C when done
284 * to signed integers.)
285 */
286 for (i = 0; i < k; i++) {
287 assert(sc->contents[yx1*k+i] < 0 || sc->contents[yx2*k+i] < 0);
288 sc->contents[yxnew*k+i] = (sc->contents[yx1*k+i] +
289 sc->contents[yx2*k+i] + 1);
290 }
291
292 /*
293 * We must combine the rows _and_ the columns in the disconnect
294 * matrix.
295 */
296 for (i = 0; i < wh; i++)
297 sc->disconnect[yxnew*wh+i] = (sc->disconnect[yx1*wh+i] ||
298 sc->disconnect[yx2*wh+i]);
299 for (i = 0; i < wh; i++)
300 sc->disconnect[i*wh+yxnew] = (sc->disconnect[i*wh+yx1] ||
301 sc->disconnect[i*wh+yx2]);
302}
303
304static void solver_disconnect(struct solver_scratch *sc, int yx1, int yx2)
305{
306 int w = sc->w, h = sc->h;
307 int wh = w*h;
308
309 yx1 = dsf_canonify(sc->dsf, yx1);
310 yx2 = dsf_canonify(sc->dsf, yx2);
311 assert(yx1 != yx2);
312 assert(!sc->disconnect[yx1*wh+yx2]);
313 assert(!sc->disconnect[yx2*wh+yx1]);
314
315 /*
316 * Mark the components as disconnected from each other in the
317 * disconnect matrix.
318 */
319 sc->disconnect[yx1*wh+yx2] = true;
320 sc->disconnect[yx2*wh+yx1] = true;
321}
322
323static void solver_init(struct solver_scratch *sc)
324{
325 int w = sc->w, h = sc->h;
326 int wh = w*h;
327 int i;
328
329 /*
330 * Set up most of the scratch space. We don't set up the
331 * contents array, however, because this will change if we
332 * adjust the letter arrangement and re-run the solver.
333 */
334 dsf_reinit(sc->dsf);
335 for (i = 0; i < wh; i++) sc->size[i] = 1;
336 memset(sc->disconnect, 0, wh*wh * sizeof(bool));
337}
338
339static int solver_attempt(struct solver_scratch *sc, const unsigned char *grid,
340 bool *gen_lock)
341{
342 int w = sc->w, h = sc->h, k = sc->k;
343 int wh = w*h;
344 int i, x, y;
345 bool done_something_overall = false;
346
347 /*
348 * Set up the contents array from the grid.
349 */
350 for (i = 0; i < wh*k; i++)
351 sc->contents[i] = -1;
352 for (i = 0; i < wh; i++)
353 sc->contents[dsf_canonify(sc->dsf, i)*k+grid[i]] = i;
354
355 while (1) {
356 bool done_something = false;
357
358 /*
359 * Go over the grid looking for reasons to add to the
360 * disconnect matrix. We're after pairs of squares which:
361 *
362 * - are adjacent in the grid
363 * - belong to distinct dsf components
364 * - their components are not already marked as
365 * disconnected
366 * - their components share a letter in common.
367 */
368 for (y = 0; y < h; y++) {
369 for (x = 0; x < w; x++) {
370 int dir;
371 for (dir = 0; dir < 2; dir++) {
372 int x2 = x + dir, y2 = y + 1 - dir;
373 int yx = y*w+x, yx2 = y2*w+x2;
374
375 if (x2 >= w || y2 >= h)
376 continue; /* one square is outside the grid */
377
378 yx = dsf_canonify(sc->dsf, yx);
379 yx2 = dsf_canonify(sc->dsf, yx2);
380 if (yx == yx2)
381 continue; /* same dsf component */
382
383 if (sc->disconnect[yx*wh+yx2])
384 continue; /* already known disconnected */
385
386 for (i = 0; i < k; i++)
387 if (sc->contents[yx*k+i] >= 0 &&
388 sc->contents[yx2*k+i] >= 0)
389 break;
390 if (i == k)
391 continue; /* no letter in common */
392
393 /*
394 * We've found one. Mark yx and yx2 as
395 * disconnected from each other.
396 */
397#ifdef SOLVER_DIAGNOSTICS
398 printf("Disconnecting %d and %d (%c)\n", yx, yx2, 'A'+i);
399#endif
400 solver_disconnect(sc, yx, yx2);
401 done_something = done_something_overall = true;
402
403 /*
404 * We have just made a deduction which hinges
405 * on two particular grid squares being the
406 * same. If we are feeding back to a generator
407 * loop, we must therefore mark those squares
408 * as fixed in the generator, so that future
409 * rearrangement of the grid will not break
410 * the information on which we have already
411 * based deductions.
412 */
413 if (gen_lock) {
414 gen_lock[sc->contents[yx*k+i]] = true;
415 gen_lock[sc->contents[yx2*k+i]] = true;
416 }
417 }
418 }
419 }
420
421 /*
422 * Now go over the grid looking for dsf components which
423 * are below maximum size and only have one way to extend,
424 * and extending them.
425 */
426 for (i = 0; i < wh; i++)
427 sc->tmp[i] = -1;
428 for (y = 0; y < h; y++) {
429 for (x = 0; x < w; x++) {
430 int yx = dsf_canonify(sc->dsf, y*w+x);
431 int dir;
432
433 if (sc->size[yx] == k)
434 continue;
435
436 for (dir = 0; dir < 4; dir++) {
437 int x2 = x + (dir==0 ? -1 : dir==2 ? 1 : 0);
438 int y2 = y + (dir==1 ? -1 : dir==3 ? 1 : 0);
439 int yx2, yx2c;
440
441 if (y2 < 0 || y2 >= h || x2 < 0 || x2 >= w)
442 continue;
443 yx2 = y2*w+x2;
444 yx2c = dsf_canonify(sc->dsf, yx2);
445
446 if (yx2c != yx && !sc->disconnect[yx2c*wh+yx]) {
447 /*
448 * Component yx can be extended into square
449 * yx2.
450 */
451 if (sc->tmp[yx] == -1)
452 sc->tmp[yx] = yx2;
453 else if (sc->tmp[yx] != yx2)
454 sc->tmp[yx] = -2; /* multiple choices found */
455 }
456 }
457 }
458 }
459 for (i = 0; i < wh; i++) {
460 if (sc->tmp[i] >= 0) {
461 /*
462 * Make sure we haven't connected the two already
463 * during this loop (which could happen if for
464 * _both_ components this was the only way to
465 * extend them).
466 */
467 if (dsf_canonify(sc->dsf, i) ==
468 dsf_canonify(sc->dsf, sc->tmp[i]))
469 continue;
470
471#ifdef SOLVER_DIAGNOSTICS
472 printf("Connecting %d and %d\n", i, sc->tmp[i]);
473#endif
474 solver_connect(sc, i, sc->tmp[i]);
475 done_something = done_something_overall = true;
476 break;
477 }
478 }
479
480 if (!done_something)
481 break;
482 }
483
484 /*
485 * Return 0 if we haven't made any progress; 1 if we've done
486 * something but not solved it completely; 2 if we've solved
487 * it completely.
488 */
489 for (i = 0; i < wh; i++)
490 if (sc->size[dsf_canonify(sc->dsf, i)] != k)
491 break;
492 if (i == wh)
493 return 2;
494 if (done_something_overall)
495 return 1;
496 return 0;
497}
498
499static unsigned char *generate(int w, int h, int k, random_state *rs)
500{
501 int wh = w*h;
502 int n = wh/k;
503 struct solver_scratch *sc;
504 unsigned char *grid;
505 unsigned char *shuffled;
506 int i, j, m, retries;
507 int *permutation;
508 bool *gen_lock;
509
510 sc = solver_scratch_new(w, h, k);
511 grid = snewn(wh, unsigned char);
512 shuffled = snewn(k, unsigned char);
513 permutation = snewn(wh, int);
514 gen_lock = snewn(wh, bool);
515
516 do {
517 DSF *dsf = divvy_rectangle(w, h, k, rs);
518
519 /*
520 * Go through the dsf and find the indices of all the
521 * squares involved in each omino, in a manner conducive
522 * to per-omino indexing. We set permutation[i*k+j] to be
523 * the index of the jth square (ordered arbitrarily) in
524 * omino i.
525 */
526 for (i = j = 0; i < wh; i++)
527 if (dsf_canonify(dsf, i) == i) {
528 sc->tmp[i] = j;
529 /*
530 * During this loop and the following one, we use
531 * the last element of each row of permutation[]
532 * as a counter of the number of indices so far
533 * placed in it. When we place the final index of
534 * an omino, that counter is overwritten, but that
535 * doesn't matter because we'll never use it
536 * again. Of course this depends critically on
537 * divvy_rectangle() having returned correct
538 * results, or else chaos would ensue.
539 */
540 permutation[j*k+k-1] = 0;
541 j++;
542 }
543 for (i = 0; i < wh; i++) {
544 j = sc->tmp[dsf_canonify(dsf, i)];
545 m = permutation[j*k+k-1]++;
546 permutation[j*k+m] = i;
547 }
548
549 /*
550 * Track which squares' letters we have already depended
551 * on for deductions. This is gradually updated by
552 * solver_attempt().
553 */
554 memset(gen_lock, 0, wh * sizeof(bool));
555
556 /*
557 * Now repeatedly fill the grid with letters, and attempt
558 * to solve it. If the solver makes progress but does not
559 * fail completely, then gen_lock will have been updated
560 * and we try again. On a complete failure, though, we
561 * have no option but to give up and abandon this set of
562 * ominoes.
563 */
564 solver_init(sc);
565 retries = k*k;
566 while (1) {
567 /*
568 * Fill the grid with letters. We can safely use
569 * sc->tmp to hold the set of letters required at each
570 * stage, since it's at least size k and is currently
571 * unused.
572 */
573 for (i = 0; i < n; i++) {
574 /*
575 * First, determine the set of letters already
576 * placed in this omino by gen_lock.
577 */
578 for (j = 0; j < k; j++)
579 sc->tmp[j] = j;
580 for (j = 0; j < k; j++) {
581 int index = permutation[i*k+j];
582 int letter = grid[index];
583 if (gen_lock[index])
584 sc->tmp[letter] = -1;
585 }
586 /*
587 * Now collect together all the remaining letters
588 * and randomly shuffle them.
589 */
590 for (j = m = 0; j < k; j++)
591 if (sc->tmp[j] >= 0)
592 sc->tmp[m++] = sc->tmp[j];
593 shuffle(sc->tmp, m, sizeof(*sc->tmp), rs);
594 /*
595 * Finally, write the shuffled letters into the
596 * grid.
597 */
598 for (j = 0; j < k; j++) {
599 int index = permutation[i*k+j];
600 if (!gen_lock[index])
601 grid[index] = sc->tmp[--m];
602 }
603 assert(m == 0);
604 }
605
606 /*
607 * Now we have a candidate grid. Attempt to progress
608 * the solution.
609 */
610 m = solver_attempt(sc, grid, gen_lock);
611 if (m == 2 || /* success */
612 (m == 0 && retries-- <= 0)) /* failure */
613 break;
614 if (m == 1)
615 retries = k*k; /* reset this counter, and continue */
616 }
617
618 dsf_free(dsf);
619 } while (m == 0);
620
621 sfree(gen_lock);
622 sfree(permutation);
623 sfree(shuffled);
624 solver_scratch_free(sc);
625
626 return grid;
627}
628
629/* ----------------------------------------------------------------------
630 * End of solver/generator code.
631 */
632
633static char *new_game_desc(const game_params *params, random_state *rs,
634 char **aux, bool interactive)
635{
636 int w = params->w, h = params->h, wh = w*h, k = params->k;
637 unsigned char *grid;
638 char *desc;
639 int i;
640
641 grid = generate(w, h, k, rs);
642
643 desc = snewn(wh+1, char);
644 for (i = 0; i < wh; i++)
645 desc[i] = 'A' + grid[i];
646 desc[wh] = '\0';
647
648 sfree(grid);
649
650 return desc;
651}
652
653static const char *validate_desc(const game_params *params, const char *desc)
654{
655 return NULL;
656}
657
658static game_state *new_game(midend *me, const game_params *params,
659 const char *desc)
660{
661 game_state *state = snew(game_state);
662
663 state->FIXME = 0;
664
665 return state;
666}
667
668static game_state *dup_game(const game_state *state)
669{
670 game_state *ret = snew(game_state);
671
672 ret->FIXME = state->FIXME;
673
674 return ret;
675}
676
677static void free_game(game_state *state)
678{
679 sfree(state);
680}
681
682static char *solve_game(const game_state *state, const game_state *currstate,
683 const char *aux, const char **error)
684{
685 return NULL;
686}
687
688static bool game_can_format_as_text_now(const game_params *params)
689{
690 return true;
691}
692
693static char *game_text_format(const game_state *state)
694{
695 return NULL;
696}
697
698static game_ui *new_ui(const game_state *state)
699{
700 return NULL;
701}
702
703static void free_ui(game_ui *ui)
704{
705}
706
707static void game_changed_state(game_ui *ui, const game_state *oldstate,
708 const game_state *newstate)
709{
710}
711
712struct game_drawstate {
713 int tilesize;
714 int FIXME;
715};
716
717static char *interpret_move(const game_state *state, game_ui *ui,
718 const game_drawstate *ds,
719 int x, int y, int button)
720{
721 return NULL;
722}
723
724static game_state *execute_move(const game_state *state, const char *move)
725{
726 return NULL;
727}
728
729/* ----------------------------------------------------------------------
730 * Drawing routines.
731 */
732
733static void game_compute_size(const game_params *params, int tilesize,
734 const game_ui *ui, int *x, int *y)
735{
736 *x = *y = 10 * tilesize; /* FIXME */
737}
738
739static void game_set_size(drawing *dr, game_drawstate *ds,
740 const game_params *params, int tilesize)
741{
742 ds->tilesize = tilesize;
743}
744
745static float *game_colours(frontend *fe, int *ncolours)
746{
747 float *ret = snewn(3 * NCOLOURS, float);
748
749 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
750
751 *ncolours = NCOLOURS;
752 return ret;
753}
754
755static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
756{
757 struct game_drawstate *ds = snew(struct game_drawstate);
758
759 ds->tilesize = 0;
760 ds->FIXME = 0;
761
762 return ds;
763}
764
765static void game_free_drawstate(drawing *dr, game_drawstate *ds)
766{
767 sfree(ds);
768}
769
770static void game_redraw(drawing *dr, game_drawstate *ds,
771 const game_state *oldstate, const game_state *state,
772 int dir, const game_ui *ui,
773 float animtime, float flashtime)
774{
775}
776
777static float game_anim_length(const game_state *oldstate,
778 const game_state *newstate, int dir, game_ui *ui)
779{
780 return 0.0F;
781}
782
783static float game_flash_length(const game_state *oldstate,
784 const game_state *newstate, int dir, game_ui *ui)
785{
786 return 0.0F;
787}
788
789static void game_get_cursor_location(const game_ui *ui,
790 const game_drawstate *ds,
791 const game_state *state,
792 const game_params *params,
793 int *x, int *y, int *w, int *h)
794{
795}
796
797static int game_status(const game_state *state)
798{
799 return 0;
800}
801
802static bool game_timing_state(const game_state *state, game_ui *ui)
803{
804 return true;
805}
806
807static void game_print_size(const game_params *params, const game_ui *ui,
808 float *x, float *y)
809{
810}
811
812static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
813 int tilesize)
814{
815}
816
817#ifdef COMBINED
818#define thegame separate
819#endif
820
821const struct game thegame = {
822 "Separate", NULL, NULL,
823 default_params,
824 game_fetch_preset, NULL,
825 decode_params,
826 encode_params,
827 free_params,
828 dup_params,
829 false, game_configure, custom_params,
830 validate_params,
831 new_game_desc,
832 validate_desc,
833 new_game,
834 dup_game,
835 free_game,
836 false, solve_game,
837 false, game_can_format_as_text_now, game_text_format,
838 NULL, NULL, /* get_prefs, set_prefs */
839 new_ui,
840 free_ui,
841 NULL, /* encode_ui */
842 NULL, /* decode_ui */
843 NULL, /* game_request_keys */
844 game_changed_state,
845 NULL, /* current_key_label */
846 interpret_move,
847 execute_move,
848 20 /* FIXME */, game_compute_size, game_set_size,
849 game_colours,
850 game_new_drawstate,
851 game_free_drawstate,
852 game_redraw,
853 game_anim_length,
854 game_flash_length,
855 game_get_cursor_location,
856 game_status,
857 false, false, game_print_size, game_print,
858 false, /* wants_statusbar */
859 false, game_timing_state,
860 0, /* flags */
861};
diff --git a/apps/plugins/puzzles/src/unfinished/slide.c b/apps/plugins/puzzles/src/unfinished/slide.c
new file mode 100644
index 0000000000..4c4d98943e
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/slide.c
@@ -0,0 +1,2444 @@
1/*
2 * slide.c: Implementation of the block-sliding puzzle `Klotski'.
3 */
4
5/*
6 * TODO:
7 *
8 * - Improve the generator.
9 * * actually, we seem to be mostly sensible already now. I
10 * want more choice over the type of main block and location
11 * of the exit/target, and I think I probably ought to give
12 * up on compactness and just bite the bullet and have the
13 * target area right outside the main wall, but mostly I
14 * think it's OK.
15 * * the move limit tends to make the game _slower_ to
16 * generate, which is odd. Perhaps investigate why.
17 *
18 * - Improve the graphics.
19 * * All the colours are a bit wishy-washy. _Some_ dark
20 * colours would surely not be excessive? Probably darken
21 * the tiles, the walls and the main block, and leave the
22 * target marker pale.
23 * * The cattle grid effect is still disgusting. Think of
24 * something completely different.
25 * * The highlight for next-piece-to-move in the solver is
26 * excessive, and the shadow blends in too well with the
27 * piece lowlights. Adjust both.
28 */
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <assert.h>
34#include <ctype.h>
35#ifdef NO_TGMATH_H
36# include <math.h>
37#else
38# include <tgmath.h>
39#endif
40
41#include "puzzles.h"
42#include "tree234.h"
43
44/*
45 * The implementation of this game revolves around the insight
46 * which makes an exhaustive-search solver feasible: although
47 * there are many blocks which can be rearranged in many ways, any
48 * two blocks of the same shape are _indistinguishable_ and hence
49 * the number of _distinct_ board layouts is generally much
50 * smaller. So we adopt a representation for board layouts which
51 * is inherently canonical, i.e. there are no two distinct
52 * representations which encode indistinguishable layouts.
53 *
54 * The way we do this is to encode each square of the board, in
55 * the normal left-to-right top-to-bottom order, as being one of
56 * the following things:
57 * - the first square (in the given order) of a block (`anchor')
58 * - special case of the above: the anchor for the _main_ block
59 * (i.e. the one which the aim of the game is to get to the
60 * target position)
61 * - a subsequent square of a block whose previous square was N
62 * squares ago
63 * - an impassable wall
64 *
65 * (We also separately store data about which board positions are
66 * forcefields only passable by the main block. We can't encode
67 * that in the main board data, because then the main block would
68 * destroy forcefields as it went over them.)
69 *
70 * Hence, for example, a 2x2 square block would be encoded as
71 * ANCHOR, followed by DIST(1), and w-2 squares later on there
72 * would be DIST(w-1) followed by DIST(1). So if you start at the
73 * last of those squares, the DIST numbers give you a linked list
74 * pointing back through all the other squares in the same block.
75 *
76 * So the solver simply does a bfs over all reachable positions,
77 * encoding them in this format and storing them in a tree234 to
78 * ensure it doesn't ever revisit an already-analysed position.
79 */
80
81enum {
82 /*
83 * The colours are arranged here so that every base colour is
84 * directly followed by its highlight colour and then its
85 * lowlight colour. Do not break this, or draw_tile() will get
86 * confused.
87 */
88 COL_BACKGROUND,
89 COL_HIGHLIGHT,
90 COL_LOWLIGHT,
91 COL_DRAGGING,
92 COL_DRAGGING_HIGHLIGHT,
93 COL_DRAGGING_LOWLIGHT,
94 COL_MAIN,
95 COL_MAIN_HIGHLIGHT,
96 COL_MAIN_LOWLIGHT,
97 COL_MAIN_DRAGGING,
98 COL_MAIN_DRAGGING_HIGHLIGHT,
99 COL_MAIN_DRAGGING_LOWLIGHT,
100 COL_TARGET,
101 COL_TARGET_HIGHLIGHT,
102 COL_TARGET_LOWLIGHT,
103 NCOLOURS
104};
105
106/*
107 * Board layout is a simple array of bytes. Each byte holds:
108 */
109#define ANCHOR 255 /* top-left-most square of some piece */
110#define MAINANCHOR 254 /* anchor of _main_ piece */
111#define EMPTY 253 /* empty square */
112#define WALL 252 /* immovable wall */
113#define MAXDIST 251
114/* all other values indicate distance back to previous square of same block */
115#define ISDIST(x) ( (unsigned char)((x)-1) <= MAXDIST-1 )
116#define DIST(x) (x)
117#define ISANCHOR(x) ( (x)==ANCHOR || (x)==MAINANCHOR )
118#define ISBLOCK(x) ( ISANCHOR(x) || ISDIST(x) )
119
120/*
121 * MAXDIST is the largest DIST value we can encode. This must
122 * therefore also be the maximum puzzle width in theory (although
123 * solver running time will dictate a much smaller limit in
124 * practice).
125 */
126#define MAXWID MAXDIST
127
128struct game_params {
129 int w, h;
130 int maxmoves;
131};
132
133struct game_immutable_state {
134 int refcount;
135 bool *forcefield;
136};
137
138struct game_solution {
139 int nmoves;
140 int *moves; /* just like from solve_board() */
141 int refcount;
142};
143
144struct game_state {
145 int w, h;
146 unsigned char *board;
147 int tx, ty; /* target coords for MAINANCHOR */
148 int minmoves; /* for display only */
149 int lastmoved, lastmoved_pos; /* for move counting */
150 int movecount;
151 int completed;
152 bool cheated;
153 struct game_immutable_state *imm;
154 struct game_solution *soln;
155 int soln_index;
156};
157
158static game_params *default_params(void)
159{
160 game_params *ret = snew(game_params);
161
162 ret->w = 7;
163 ret->h = 6;
164 ret->maxmoves = 40;
165
166 return ret;
167}
168
169static const struct game_params slide_presets[] = {
170 {7, 6, 25},
171 {7, 6, -1},
172 {8, 6, -1},
173};
174
175static bool game_fetch_preset(int i, char **name, game_params **params)
176{
177 game_params *ret;
178 char str[80];
179
180 if (i < 0 || i >= lenof(slide_presets))
181 return false;
182
183 ret = snew(game_params);
184 *ret = slide_presets[i];
185
186 sprintf(str, "%dx%d", ret->w, ret->h);
187 if (ret->maxmoves >= 0)
188 sprintf(str + strlen(str), ", max %d moves", ret->maxmoves);
189 else
190 sprintf(str + strlen(str), ", no move limit");
191
192 *name = dupstr(str);
193 *params = ret;
194 return true;
195}
196
197static void free_params(game_params *params)
198{
199 sfree(params);
200}
201
202static game_params *dup_params(const game_params *params)
203{
204 game_params *ret = snew(game_params);
205 *ret = *params; /* structure copy */
206 return ret;
207}
208
209static void decode_params(game_params *params, char const *string)
210{
211 params->w = params->h = atoi(string);
212 while (*string && isdigit((unsigned char)*string)) string++;
213 if (*string == 'x') {
214 string++;
215 params->h = atoi(string);
216 while (*string && isdigit((unsigned char)*string)) string++;
217 }
218 if (*string == 'm') {
219 string++;
220 params->maxmoves = atoi(string);
221 while (*string && isdigit((unsigned char)*string)) string++;
222 } else if (*string == 'u') {
223 string++;
224 params->maxmoves = -1;
225 }
226}
227
228static char *encode_params(const game_params *params, bool full)
229{
230 char data[256];
231
232 sprintf(data, "%dx%d", params->w, params->h);
233 if (params->maxmoves >= 0)
234 sprintf(data + strlen(data), "m%d", params->maxmoves);
235 else
236 sprintf(data + strlen(data), "u");
237
238 return dupstr(data);
239}
240
241static config_item *game_configure(const game_params *params)
242{
243 config_item *ret;
244 char buf[80];
245
246 ret = snewn(4, config_item);
247
248 ret[0].name = "Width";
249 ret[0].type = C_STRING;
250 sprintf(buf, "%d", params->w);
251 ret[0].u.string.sval = dupstr(buf);
252
253 ret[1].name = "Height";
254 ret[1].type = C_STRING;
255 sprintf(buf, "%d", params->h);
256 ret[1].u.string.sval = dupstr(buf);
257
258 ret[2].name = "Solution length limit";
259 ret[2].type = C_STRING;
260 sprintf(buf, "%d", params->maxmoves);
261 ret[2].u.string.sval = dupstr(buf);
262
263 ret[3].name = NULL;
264 ret[3].type = C_END;
265
266 return ret;
267}
268
269static game_params *custom_params(const config_item *cfg)
270{
271 game_params *ret = snew(game_params);
272
273 ret->w = atoi(cfg[0].u.string.sval);
274 ret->h = atoi(cfg[1].u.string.sval);
275 ret->maxmoves = atoi(cfg[2].u.string.sval);
276
277 return ret;
278}
279
280static const char *validate_params(const game_params *params, bool full)
281{
282 if (params->w > MAXWID)
283 return "Width must be at most " STR(MAXWID);
284
285 if (params->w < 5)
286 return "Width must be at least 5";
287 if (params->h < 4)
288 return "Height must be at least 4";
289
290 return NULL;
291}
292
293static char *board_text_format(int w, int h, unsigned char *data,
294 bool *forcefield)
295{
296 int wh = w*h;
297 DSF *dsf = dsf_new(wh);
298 int i, x, y;
299 int retpos, retlen = (w*2+2)*(h*2+1)+1;
300 char *ret = snewn(retlen, char);
301
302 for (i = 0; i < wh; i++)
303 if (ISDIST(data[i]))
304 dsf_merge(dsf, i - data[i], i);
305 retpos = 0;
306 for (y = 0; y < 2*h+1; y++) {
307 for (x = 0; x < 2*w+1; x++) {
308 int v;
309 int i = (y/2)*w+(x/2);
310
311#define dtype(i) (ISBLOCK(data[i]) ? \
312 dsf_canonify(dsf, i) : data[i])
313#define dchar(t) ((t)==EMPTY ? ' ' : (t)==WALL ? '#' : \
314 data[t] == MAINANCHOR ? '*' : '%')
315
316 if (y % 2 && x % 2) {
317 int j = dtype(i);
318 v = dchar(j);
319 } else if (y % 2 && !(x % 2)) {
320 int j1 = (x > 0 ? dtype(i-1) : -1);
321 int j2 = (x < 2*w ? dtype(i) : -1);
322 if (j1 != j2)
323 v = '|';
324 else
325 v = dchar(j1);
326 } else if (!(y % 2) && (x % 2)) {
327 int j1 = (y > 0 ? dtype(i-w) : -1);
328 int j2 = (y < 2*h ? dtype(i) : -1);
329 if (j1 != j2)
330 v = '-';
331 else
332 v = dchar(j1);
333 } else {
334 int j1 = (x > 0 && y > 0 ? dtype(i-w-1) : -1);
335 int j2 = (x > 0 && y < 2*h ? dtype(i-1) : -1);
336 int j3 = (x < 2*w && y > 0 ? dtype(i-w) : -1);
337 int j4 = (x < 2*w && y < 2*h ? dtype(i) : -1);
338 if (j1 == j2 && j2 == j3 && j3 == j4)
339 v = dchar(j1);
340 else if (j1 == j2 && j3 == j4)
341 v = '|';
342 else if (j1 == j3 && j2 == j4)
343 v = '-';
344 else
345 v = '+';
346 }
347
348 assert(retpos < retlen);
349 ret[retpos++] = v;
350 }
351 assert(retpos < retlen);
352 ret[retpos++] = '\n';
353 }
354 assert(retpos < retlen);
355 ret[retpos++] = '\0';
356 assert(retpos == retlen);
357
358 return ret;
359}
360
361/* ----------------------------------------------------------------------
362 * Solver.
363 */
364
365/*
366 * During solver execution, the set of visited board positions is
367 * stored as a tree234 of the following structures. `w', `h' and
368 * `data' are obvious in meaning; `dist' represents the minimum
369 * distance to reach this position from the starting point.
370 *
371 * `prev' links each board to the board position from which it was
372 * most efficiently derived.
373 */
374struct board {
375 int w, h;
376 int dist;
377 struct board *prev;
378 unsigned char *data;
379};
380
381static int boardcmp(void *av, void *bv)
382{
383 struct board *a = (struct board *)av;
384 struct board *b = (struct board *)bv;
385 return memcmp(a->data, b->data, a->w * a->h);
386}
387
388static struct board *newboard(int w, int h, unsigned char *data)
389{
390 struct board *b = malloc(sizeof(struct board) + w*h);
391 b->data = (unsigned char *)b + sizeof(struct board);
392 memcpy(b->data, data, w*h);
393 b->w = w;
394 b->h = h;
395 b->dist = -1;
396 b->prev = NULL;
397 return b;
398}
399
400/*
401 * The actual solver. Given a board, attempt to find the minimum
402 * length of move sequence which moves MAINANCHOR to (tx,ty), or
403 * -1 if no solution exists. Returns that minimum length.
404 *
405 * Also, if `moveout' is provided, writes out the moves in the
406 * form of a sequence of pairs of integers indicating the source
407 * and destination points of the anchor of the moved piece in each
408 * move. Exactly twice as many integers are written as the number
409 * returned from solve_board(), and `moveout' receives an int *
410 * which is a pointer to a dynamically allocated array.
411 */
412static int solve_board(int w, int h, unsigned char *board,
413 bool *forcefield, int tx, int ty,
414 int movelimit, int **moveout)
415{
416 int wh = w*h;
417 struct board *b, *b2, *b3;
418 int *next, *which;
419 bool *anchors, *movereached;
420 int *movequeue, mqhead, mqtail;
421 tree234 *sorted, *queue;
422 int i, j, dir;
423 int qlen, lastdist;
424 int ret;
425
426#ifdef SOLVER_DIAGNOSTICS
427 {
428 char *t = board_text_format(w, h, board);
429 for (i = 0; i < h; i++) {
430 for (j = 0; j < w; j++) {
431 int c = board[i*w+j];
432 if (ISDIST(c))
433 printf("D%-3d", c);
434 else if (c == MAINANCHOR)
435 printf("M ");
436 else if (c == ANCHOR)
437 printf("A ");
438 else if (c == WALL)
439 printf("W ");
440 else if (c == EMPTY)
441 printf("E ");
442 }
443 printf("\n");
444 }
445
446 printf("Starting solver for:\n%s\n", t);
447 sfree(t);
448 }
449#endif
450
451 sorted = newtree234(boardcmp);
452 queue = newtree234(NULL);
453
454 b = newboard(w, h, board);
455 b->dist = 0;
456 add234(sorted, b);
457 addpos234(queue, b, 0);
458 qlen = 1;
459
460 next = snewn(wh, int);
461 anchors = snewn(wh, bool);
462 which = snewn(wh, int);
463 movereached = snewn(wh, bool);
464 movequeue = snewn(wh, int);
465 lastdist = -1;
466
467 while ((b = delpos234(queue, 0)) != NULL) {
468 qlen--;
469 if (movelimit >= 0 && b->dist >= movelimit) {
470 /*
471 * The problem is not soluble in under `movelimit'
472 * moves, so we can quit right now.
473 */
474 b2 = NULL;
475 goto done;
476 }
477 if (b->dist != lastdist) {
478#ifdef SOLVER_DIAGNOSTICS
479 printf("dist %d (%d)\n", b->dist, count234(sorted));
480#endif
481 lastdist = b->dist;
482 }
483 /*
484 * Find all the anchors and form a linked list of the
485 * squares within each block.
486 */
487 for (i = 0; i < wh; i++) {
488 next[i] = -1;
489 anchors[i] = false;
490 which[i] = -1;
491 if (ISANCHOR(b->data[i])) {
492 anchors[i] = true;
493 which[i] = i;
494 } else if (ISDIST(b->data[i])) {
495 j = i - b->data[i];
496 next[j] = i;
497 which[i] = which[j];
498 }
499 }
500
501 /*
502 * For each anchor, do an array-based BFS to find all the
503 * places we can slide it to.
504 */
505 for (i = 0; i < wh; i++) {
506 if (!anchors[i])
507 continue;
508
509 mqhead = mqtail = 0;
510 for (j = 0; j < wh; j++)
511 movereached[j] = false;
512 movequeue[mqtail++] = i;
513 while (mqhead < mqtail) {
514 int pos = movequeue[mqhead++];
515
516 /*
517 * Try to move in each direction from here.
518 */
519 for (dir = 0; dir < 4; dir++) {
520 int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0);
521 int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0);
522 int offset = dy*w + dx;
523 int newpos = pos + offset;
524 int d = newpos - i;
525
526 /*
527 * For each square involved in this block,
528 * check to see if the square d spaces away
529 * from it is either empty or part of the same
530 * block.
531 */
532 for (j = i; j >= 0; j = next[j]) {
533 int jy = (pos+j-i) / w + dy, jx = (pos+j-i) % w + dx;
534 if (jy >= 0 && jy < h && jx >= 0 && jx < w &&
535 ((b->data[j+d] == EMPTY || which[j+d] == i) &&
536 (b->data[i] == MAINANCHOR || !forcefield[j+d])))
537 /* ok */;
538 else
539 break;
540 }
541 if (j >= 0)
542 continue; /* this direction wasn't feasible */
543
544 /*
545 * If we've already tried moving this piece
546 * here, leave it.
547 */
548 if (movereached[newpos])
549 continue;
550 movereached[newpos] = true;
551 movequeue[mqtail++] = newpos;
552
553 /*
554 * We have a viable move. Make it.
555 */
556 b2 = newboard(w, h, b->data);
557 for (j = i; j >= 0; j = next[j])
558 b2->data[j] = EMPTY;
559 for (j = i; j >= 0; j = next[j])
560 b2->data[j+d] = b->data[j];
561
562 b3 = add234(sorted, b2);
563 if (b3 != b2) {
564 sfree(b2); /* we already got one */
565 } else {
566 b2->dist = b->dist + 1;
567 b2->prev = b;
568 addpos234(queue, b2, qlen++);
569 if (b2->data[ty*w+tx] == MAINANCHOR)
570 goto done; /* search completed! */
571 }
572 }
573 }
574 }
575 }
576 b2 = NULL;
577
578 done:
579
580 if (b2) {
581 ret = b2->dist;
582 if (moveout) {
583 /*
584 * Now b2 represents the solved position. Backtrack to
585 * output the solution.
586 */
587 *moveout = snewn(ret * 2, int);
588 j = ret * 2;
589
590 while (b2->prev) {
591 int from = -1, to = -1;
592
593 b = b2->prev;
594
595 /*
596 * Scan b and b2 to find out which piece has
597 * moved.
598 */
599 for (i = 0; i < wh; i++) {
600 if (ISANCHOR(b->data[i]) && !ISANCHOR(b2->data[i])) {
601 assert(from == -1);
602 from = i;
603 } else if (!ISANCHOR(b->data[i]) && ISANCHOR(b2->data[i])){
604 assert(to == -1);
605 to = i;
606 }
607 }
608
609 assert(from >= 0 && to >= 0);
610 assert(j >= 2);
611 (*moveout)[--j] = to;
612 (*moveout)[--j] = from;
613
614 b2 = b;
615 }
616 assert(j == 0);
617 }
618 } else {
619 ret = -1; /* no solution */
620 if (moveout)
621 *moveout = NULL;
622 }
623
624 freetree234(queue);
625
626 while ((b = delpos234(sorted, 0)) != NULL)
627 sfree(b);
628 freetree234(sorted);
629
630 sfree(next);
631 sfree(anchors);
632 sfree(movereached);
633 sfree(movequeue);
634 sfree(which);
635
636 return ret;
637}
638
639/* ----------------------------------------------------------------------
640 * Random board generation.
641 */
642
643static void generate_board(int w, int h, int *rtx, int *rty, int *minmoves,
644 random_state *rs, unsigned char **rboard,
645 bool **rforcefield, int movelimit)
646{
647 int wh = w*h;
648 unsigned char *board, *board2;
649 bool *forcefield;
650 bool *tried_merge;
651 DSF *dsf;
652 int *list, nlist, pos;
653 int tx, ty;
654 int i, j;
655 int moves = 0; /* placate optimiser */
656
657 /*
658 * Set up a board and fill it with singletons, except for a
659 * border of walls.
660 */
661 board = snewn(wh, unsigned char);
662 forcefield = snewn(wh, bool);
663 board2 = snewn(wh, unsigned char);
664 memset(board, ANCHOR, wh);
665 memset(forcefield, 0, wh * sizeof(bool));
666 for (i = 0; i < w; i++)
667 board[i] = board[i+w*(h-1)] = WALL;
668 for (i = 0; i < h; i++)
669 board[i*w] = board[i*w+(w-1)] = WALL;
670
671 tried_merge = snewn(wh * wh, bool);
672 memset(tried_merge, 0, wh*wh * sizeof(bool));
673 dsf = dsf_new(wh);
674
675 /*
676 * Invent a main piece at one extreme. (FIXME: vary the
677 * extreme, and the piece.)
678 */
679 board[w+1] = MAINANCHOR;
680 board[w+2] = DIST(1);
681 board[w*2+1] = DIST(w-1);
682 board[w*2+2] = DIST(1);
683
684 /*
685 * Invent a target position. (FIXME: vary this too.)
686 */
687 tx = w-2;
688 ty = h-3;
689 forcefield[ty*w+tx+1] = true;
690 forcefield[(ty+1)*w+tx+1] = true;
691 board[ty*w+tx+1] = board[(ty+1)*w+tx+1] = EMPTY;
692
693 /*
694 * Gradually remove singletons until the game becomes soluble.
695 */
696 for (j = w; j-- > 0 ;)
697 for (i = h; i-- > 0 ;)
698 if (board[i*w+j] == ANCHOR) {
699 /*
700 * See if the board is already soluble.
701 */
702 if ((moves = solve_board(w, h, board, forcefield,
703 tx, ty, movelimit, NULL)) >= 0)
704 goto soluble;
705
706 /*
707 * Otherwise, remove this piece.
708 */
709 board[i*w+j] = EMPTY;
710 }
711 assert(!"We shouldn't get here");
712 soluble:
713
714 /*
715 * Make a list of all the inter-block edges on the board.
716 */
717 list = snewn(wh*2, int);
718 nlist = 0;
719 for (i = 0; i+1 < w; i++)
720 for (j = 0; j < h; j++)
721 list[nlist++] = (j*w+i) * 2 + 0; /* edge to the right of j*w+i */
722 for (j = 0; j+1 < h; j++)
723 for (i = 0; i < w; i++)
724 list[nlist++] = (j*w+i) * 2 + 1; /* edge below j*w+i */
725
726 /*
727 * Now go through that list in random order, trying to merge
728 * the blocks on each side of each edge.
729 */
730 shuffle(list, nlist, sizeof(*list), rs);
731 while (nlist > 0) {
732 int x1, y1, p1, c1;
733 int x2, y2, p2, c2;
734
735 pos = list[--nlist];
736 y1 = y2 = pos / (w*2);
737 x1 = x2 = (pos / 2) % w;
738 if (pos % 2)
739 y2++;
740 else
741 x2++;
742 p1 = y1*w+x1;
743 p2 = y2*w+x2;
744
745 /*
746 * Immediately abandon the attempt if we've already tried
747 * to merge the same pair of blocks along a different
748 * edge.
749 */
750 c1 = dsf_canonify(dsf, p1);
751 c2 = dsf_canonify(dsf, p2);
752 if (tried_merge[c1 * wh + c2])
753 continue;
754
755 /*
756 * In order to be mergeable, these two squares must each
757 * either be, or belong to, a non-main anchor, and their
758 * anchors must also be distinct.
759 */
760 if (!ISBLOCK(board[p1]) || !ISBLOCK(board[p2]))
761 continue;
762 while (ISDIST(board[p1]))
763 p1 -= board[p1];
764 while (ISDIST(board[p2]))
765 p2 -= board[p2];
766 if (board[p1] == MAINANCHOR || board[p2] == MAINANCHOR || p1 == p2)
767 continue;
768
769 /*
770 * We can merge these blocks. Try it, and see if the
771 * puzzle remains soluble.
772 */
773 memcpy(board2, board, wh);
774 j = -1;
775 while (p1 < wh || p2 < wh) {
776 /*
777 * p1 and p2 are the squares at the head of each block
778 * list. Pick the smaller one and put it on the output
779 * block list.
780 */
781 i = min(p1, p2);
782 if (j < 0) {
783 board[i] = ANCHOR;
784 } else {
785 assert(i - j <= MAXDIST);
786 board[i] = DIST(i - j);
787 }
788 j = i;
789
790 /*
791 * Now advance whichever list that came from.
792 */
793 if (i == p1) {
794 do {
795 p1++;
796 } while (p1 < wh && board[p1] != DIST(p1-i));
797 } else {
798 do {
799 p2++;
800 } while (p2 < wh && board[p2] != DIST(p2-i));
801 }
802 }
803 j = solve_board(w, h, board, forcefield, tx, ty, movelimit, NULL);
804 if (j < 0) {
805 /*
806 * Didn't work. Revert the merge.
807 */
808 memcpy(board, board2, wh);
809 tried_merge[c1 * wh + c2] = true;
810 tried_merge[c2 * wh + c1] = true;
811 } else {
812 int c;
813
814 moves = j;
815
816 dsf_merge(dsf, c1, c2);
817 c = dsf_canonify(dsf, c1);
818 for (i = 0; i < wh; i++)
819 tried_merge[c*wh+i] = (tried_merge[c1*wh+i] ||
820 tried_merge[c2*wh+i]);
821 for (i = 0; i < wh; i++)
822 tried_merge[i*wh+c] = (tried_merge[i*wh+c1] ||
823 tried_merge[i*wh+c2]);
824 }
825 }
826
827 dsf_free(dsf);
828 sfree(list);
829 sfree(tried_merge);
830 sfree(board2);
831
832 *rtx = tx;
833 *rty = ty;
834 *rboard = board;
835 *rforcefield = forcefield;
836 *minmoves = moves;
837}
838
839/* ----------------------------------------------------------------------
840 * End of solver/generator code.
841 */
842
843static char *new_game_desc(const game_params *params, random_state *rs,
844 char **aux, bool interactive)
845{
846 int w = params->w, h = params->h, wh = w*h;
847 int tx, ty, minmoves;
848 unsigned char *board;
849 bool *forcefield;
850 char *ret, *p;
851 int i;
852
853 generate_board(params->w, params->h, &tx, &ty, &minmoves, rs,
854 &board, &forcefield, params->maxmoves);
855#ifdef GENERATOR_DIAGNOSTICS
856 {
857 char *t = board_text_format(params->w, params->h, board);
858 printf("%s\n", t);
859 sfree(t);
860 }
861#endif
862
863 /*
864 * Encode as a game ID.
865 */
866 ret = snewn(wh * 6 + 40, char);
867 p = ret;
868 i = 0;
869 while (i < wh) {
870 if (ISDIST(board[i])) {
871 p += sprintf(p, "d%d", board[i]);
872 i++;
873 } else {
874 int count = 1;
875 int b = board[i];
876 bool f = forcefield[i];
877 int c = (b == ANCHOR ? 'a' :
878 b == MAINANCHOR ? 'm' :
879 b == EMPTY ? 'e' :
880 /* b == WALL ? */ 'w');
881 if (f) *p++ = 'f';
882 *p++ = c;
883 i++;
884 while (i < wh && board[i] == b && forcefield[i] == f)
885 i++, count++;
886 if (count > 1)
887 p += sprintf(p, "%d", count);
888 }
889 }
890 p += sprintf(p, ",%d,%d,%d", tx, ty, minmoves);
891 ret = sresize(ret, p+1 - ret, char);
892
893 sfree(board);
894 sfree(forcefield);
895
896 return ret;
897}
898
899static const char *validate_desc(const game_params *params, const char *desc)
900{
901 int w = params->w, h = params->h, wh = w*h;
902 bool *active;
903 int *link;
904 int mains = 0;
905 int i, tx, ty, minmoves;
906 const char *ret;
907
908 active = snewn(wh, bool);
909 link = snewn(wh, int);
910 i = 0;
911
912 while (*desc && *desc != ',') {
913 if (i >= wh) {
914 ret = "Too much data in game description";
915 goto done;
916 }
917 link[i] = -1;
918 active[i] = false;
919 if (*desc == 'f' || *desc == 'F') {
920 desc++;
921 if (!*desc) {
922 ret = "Expected another character after 'f' in game "
923 "description";
924 goto done;
925 }
926 }
927
928 if (*desc == 'd' || *desc == 'D') {
929 int dist;
930
931 desc++;
932 if (!isdigit((unsigned char)*desc)) {
933 ret = "Expected a number after 'd' in game description";
934 goto done;
935 }
936 dist = atoi(desc);
937 while (*desc && isdigit((unsigned char)*desc)) desc++;
938
939 if (dist <= 0 || dist > i) {
940 ret = "Out-of-range number after 'd' in game description";
941 goto done;
942 }
943
944 if (!active[i - dist]) {
945 ret = "Invalid back-reference in game description";
946 goto done;
947 }
948
949 link[i] = i - dist;
950
951 active[i] = true;
952 active[link[i]] = false;
953 i++;
954 } else {
955 int c = *desc++;
956 int count = 1;
957
958 if (!strchr("aAmMeEwW", c)) {
959 ret = "Invalid character in game description";
960 goto done;
961 }
962 if (isdigit((unsigned char)*desc)) {
963 count = atoi(desc);
964 while (*desc && isdigit((unsigned char)*desc)) desc++;
965 }
966 if (i + count > wh) {
967 ret = "Too much data in game description";
968 goto done;
969 }
970 while (count-- > 0) {
971 active[i] = (strchr("aAmM", c) != NULL);
972 link[i] = -1;
973 if (strchr("mM", c) != NULL) {
974 mains++;
975 }
976 i++;
977 }
978 }
979 }
980 if (mains != 1) {
981 ret = (mains == 0 ? "No main piece specified in game description" :
982 "More than one main piece specified in game description");
983 goto done;
984 }
985 if (i < wh) {
986 ret = "Not enough data in game description";
987 goto done;
988 }
989
990 /*
991 * Now read the target coordinates.
992 */
993 i = sscanf(desc, ",%d,%d,%d", &tx, &ty, &minmoves);
994 if (i < 2) {
995 ret = "No target coordinates specified";
996 goto done;
997 /*
998 * (but minmoves is optional)
999 */
1000 }
1001
1002 ret = NULL;
1003
1004 done:
1005 sfree(active);
1006 sfree(link);
1007 return ret;
1008}
1009
1010static game_state *new_game(midend *me, const game_params *params,
1011 const char *desc)
1012{
1013 int w = params->w, h = params->h, wh = w*h;
1014 game_state *state;
1015 int i;
1016
1017 state = snew(game_state);
1018 state->w = w;
1019 state->h = h;
1020 state->board = snewn(wh, unsigned char);
1021 state->lastmoved = state->lastmoved_pos = -1;
1022 state->movecount = 0;
1023 state->imm = snew(struct game_immutable_state);
1024 state->imm->refcount = 1;
1025 state->imm->forcefield = snewn(wh, bool);
1026
1027 i = 0;
1028
1029 while (*desc && *desc != ',') {
1030 bool f = false;
1031
1032 assert(i < wh);
1033
1034 if (*desc == 'f') {
1035 f = true;
1036 desc++;
1037 assert(*desc);
1038 }
1039
1040 if (*desc == 'd' || *desc == 'D') {
1041 int dist;
1042
1043 desc++;
1044 dist = atoi(desc);
1045 while (*desc && isdigit((unsigned char)*desc)) desc++;
1046
1047 state->board[i] = DIST(dist);
1048 state->imm->forcefield[i] = f;
1049
1050 i++;
1051 } else {
1052 int c = *desc++;
1053 int count = 1;
1054
1055 if (isdigit((unsigned char)*desc)) {
1056 count = atoi(desc);
1057 while (*desc && isdigit((unsigned char)*desc)) desc++;
1058 }
1059 assert(i + count <= wh);
1060
1061 c = (c == 'a' || c == 'A' ? ANCHOR :
1062 c == 'm' || c == 'M' ? MAINANCHOR :
1063 c == 'e' || c == 'E' ? EMPTY :
1064 /* c == 'w' || c == 'W' ? */ WALL);
1065
1066 while (count-- > 0) {
1067 state->board[i] = c;
1068 state->imm->forcefield[i] = f;
1069 i++;
1070 }
1071 }
1072 }
1073
1074 /*
1075 * Now read the target coordinates.
1076 */
1077 state->tx = state->ty = 0;
1078 state->minmoves = -1;
1079 i = sscanf(desc, ",%d,%d,%d", &state->tx, &state->ty, &state->minmoves);
1080
1081 if (state->board[state->ty*w+state->tx] == MAINANCHOR)
1082 state->completed = 0; /* already complete! */
1083 else
1084 state->completed = -1;
1085
1086 state->cheated = false;
1087 state->soln = NULL;
1088 state->soln_index = -1;
1089
1090 return state;
1091}
1092
1093static game_state *dup_game(const game_state *state)
1094{
1095 int w = state->w, h = state->h, wh = w*h;
1096 game_state *ret = snew(game_state);
1097
1098 ret->w = state->w;
1099 ret->h = state->h;
1100 ret->board = snewn(wh, unsigned char);
1101 memcpy(ret->board, state->board, wh);
1102 ret->tx = state->tx;
1103 ret->ty = state->ty;
1104 ret->minmoves = state->minmoves;
1105 ret->lastmoved = state->lastmoved;
1106 ret->lastmoved_pos = state->lastmoved_pos;
1107 ret->movecount = state->movecount;
1108 ret->completed = state->completed;
1109 ret->cheated = state->cheated;
1110 ret->imm = state->imm;
1111 ret->imm->refcount++;
1112 ret->soln = state->soln;
1113 ret->soln_index = state->soln_index;
1114 if (ret->soln)
1115 ret->soln->refcount++;
1116
1117 return ret;
1118}
1119
1120static void free_game(game_state *state)
1121{
1122 if (--state->imm->refcount <= 0) {
1123 sfree(state->imm->forcefield);
1124 sfree(state->imm);
1125 }
1126 if (state->soln && --state->soln->refcount <= 0) {
1127 sfree(state->soln->moves);
1128 sfree(state->soln);
1129 }
1130 sfree(state->board);
1131 sfree(state);
1132}
1133
1134static char *solve_game(const game_state *state, const game_state *currstate,
1135 const char *aux, const char **error)
1136{
1137 int *moves;
1138 int nmoves;
1139 int i;
1140 char *ret, *p, sep;
1141
1142 /*
1143 * Run the solver and attempt to find the shortest solution
1144 * from the current position.
1145 */
1146 nmoves = solve_board(state->w, state->h, state->board,
1147 state->imm->forcefield, state->tx, state->ty,
1148 -1, &moves);
1149
1150 if (nmoves < 0) {
1151 *error = "Unable to find a solution to this puzzle";
1152 return NULL;
1153 }
1154 if (nmoves == 0) {
1155 *error = "Puzzle is already solved";
1156 return NULL;
1157 }
1158
1159 /*
1160 * Encode the resulting solution as a move string.
1161 */
1162 ret = snewn(nmoves * 40, char);
1163 p = ret;
1164 sep = 'S';
1165
1166 for (i = 0; i < nmoves; i++) {
1167 p += sprintf(p, "%c%d-%d", sep, moves[i*2], moves[i*2+1]);
1168 sep = ',';
1169 }
1170
1171 sfree(moves);
1172 assert(p - ret < nmoves * 40);
1173 ret = sresize(ret, p+1 - ret, char);
1174
1175 return ret;
1176}
1177
1178static bool game_can_format_as_text_now(const game_params *params)
1179{
1180 return true;
1181}
1182
1183static char *game_text_format(const game_state *state)
1184{
1185 return board_text_format(state->w, state->h, state->board,
1186 state->imm->forcefield);
1187}
1188
1189struct game_ui {
1190 bool dragging;
1191 int drag_anchor;
1192 int drag_offset_x, drag_offset_y;
1193 int drag_currpos;
1194 bool *reachable;
1195 int *bfs_queue; /* used as scratch in interpret_move */
1196};
1197
1198static game_ui *new_ui(const game_state *state)
1199{
1200 int w = state->w, h = state->h, wh = w*h;
1201 game_ui *ui = snew(game_ui);
1202
1203 ui->dragging = false;
1204 ui->drag_anchor = ui->drag_currpos = -1;
1205 ui->drag_offset_x = ui->drag_offset_y = -1;
1206 ui->reachable = snewn(wh, bool);
1207 memset(ui->reachable, 0, wh * sizeof(bool));
1208 ui->bfs_queue = snewn(wh, int);
1209
1210 return ui;
1211}
1212
1213static void free_ui(game_ui *ui)
1214{
1215 sfree(ui->bfs_queue);
1216 sfree(ui->reachable);
1217 sfree(ui);
1218}
1219
1220static void game_changed_state(game_ui *ui, const game_state *oldstate,
1221 const game_state *newstate)
1222{
1223}
1224
1225#define PREFERRED_TILESIZE 32
1226#define TILESIZE (ds->tilesize)
1227#define BORDER (TILESIZE/2)
1228#define COORD(x) ( (x) * TILESIZE + BORDER )
1229#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
1230#define BORDER_WIDTH (1 + TILESIZE/20)
1231#define HIGHLIGHT_WIDTH (1 + TILESIZE/16)
1232
1233#define FLASH_INTERVAL 0.10F
1234#define FLASH_TIME 3*FLASH_INTERVAL
1235
1236struct game_drawstate {
1237 int tilesize;
1238 int w, h;
1239 unsigned long *grid; /* what's currently displayed */
1240};
1241
1242static char *interpret_move(const game_state *state, game_ui *ui,
1243 const game_drawstate *ds,
1244 int x, int y, int button)
1245{
1246 int w = state->w, h = state->h, wh = w*h;
1247 int tx, ty, i, j;
1248 int qhead, qtail;
1249
1250 if (button == LEFT_BUTTON) {
1251 tx = FROMCOORD(x);
1252 ty = FROMCOORD(y);
1253
1254 if (tx < 0 || tx >= w || ty < 0 || ty >= h ||
1255 !ISBLOCK(state->board[ty*w+tx]))
1256 return NULL; /* this click has no effect */
1257
1258 /*
1259 * User has clicked on a block. Find the block's anchor
1260 * and register that we've started dragging it.
1261 */
1262 i = ty*w+tx;
1263 while (ISDIST(state->board[i]))
1264 i -= state->board[i];
1265 assert(i >= 0 && i < wh);
1266
1267 ui->dragging = true;
1268 ui->drag_anchor = i;
1269 ui->drag_offset_x = tx - (i % w);
1270 ui->drag_offset_y = ty - (i / w);
1271 ui->drag_currpos = i;
1272
1273 /*
1274 * Now we immediately bfs out from the current location of
1275 * the anchor, to find all the places to which this block
1276 * can be dragged.
1277 */
1278 memset(ui->reachable, 0, wh * sizeof(bool));
1279 qhead = qtail = 0;
1280 ui->reachable[i] = true;
1281 ui->bfs_queue[qtail++] = i;
1282 for (j = i; j < wh; j++)
1283 if (state->board[j] == DIST(j - i))
1284 i = j;
1285 while (qhead < qtail) {
1286 int pos = ui->bfs_queue[qhead++];
1287 int x = pos % w, y = pos / w;
1288 int dir;
1289
1290 for (dir = 0; dir < 4; dir++) {
1291 int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0);
1292 int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0);
1293 int newpos;
1294
1295 if (x + dx < 0 || x + dx >= w ||
1296 y + dy < 0 || y + dy >= h)
1297 continue;
1298
1299 newpos = pos + dy*w + dx;
1300 if (ui->reachable[newpos])
1301 continue; /* already done this one */
1302
1303 /*
1304 * Now search the grid to see if the block we're
1305 * dragging could fit into this space.
1306 */
1307 for (j = i; j >= 0; j = (ISDIST(state->board[j]) ?
1308 j - state->board[j] : -1)) {
1309 int jx = (j+pos-ui->drag_anchor) % w;
1310 int jy = (j+pos-ui->drag_anchor) / w;
1311 int j2;
1312
1313 if (jx + dx < 0 || jx + dx >= w ||
1314 jy + dy < 0 || jy + dy >= h)
1315 break; /* this position isn't valid at all */
1316
1317 j2 = (j+pos-ui->drag_anchor) + dy*w + dx;
1318
1319 if (state->board[j2] == EMPTY &&
1320 (!state->imm->forcefield[j2] ||
1321 state->board[ui->drag_anchor] == MAINANCHOR))
1322 continue;
1323 while (ISDIST(state->board[j2]))
1324 j2 -= state->board[j2];
1325 assert(j2 >= 0 && j2 < wh);
1326 if (j2 == ui->drag_anchor)
1327 continue;
1328 else
1329 break;
1330 }
1331
1332 if (j < 0) {
1333 /*
1334 * If we got to the end of that loop without
1335 * disqualifying this position, mark it as
1336 * reachable for this drag.
1337 */
1338 ui->reachable[newpos] = true;
1339 ui->bfs_queue[qtail++] = newpos;
1340 }
1341 }
1342 }
1343
1344 /*
1345 * And that's it. Update the display to reflect the start
1346 * of a drag.
1347 */
1348 return MOVE_UI_UPDATE;
1349 } else if (button == LEFT_DRAG && ui->dragging) {
1350 int dist, distlimit, dx, dy, s, px, py;
1351
1352 tx = FROMCOORD(x);
1353 ty = FROMCOORD(y);
1354
1355 tx -= ui->drag_offset_x;
1356 ty -= ui->drag_offset_y;
1357
1358 /*
1359 * Now search outwards from (tx,ty), in order of Manhattan
1360 * distance, until we find a reachable square.
1361 */
1362 distlimit = w+tx;
1363 distlimit = max(distlimit, h+ty);
1364 distlimit = max(distlimit, tx);
1365 distlimit = max(distlimit, ty);
1366 for (dist = 0; dist <= distlimit; dist++) {
1367 for (dx = -dist; dx <= dist; dx++)
1368 for (s = -1; s <= +1; s += 2) {
1369 dy = s * (dist - abs(dx));
1370 px = tx + dx;
1371 py = ty + dy;
1372 if (px >= 0 && px < w && py >= 0 && py < h &&
1373 ui->reachable[py*w+px]) {
1374 ui->drag_currpos = py*w+px;
1375 return MOVE_UI_UPDATE;
1376 }
1377 }
1378 }
1379 return NULL; /* give up - this drag has no effect */
1380 } else if (button == LEFT_RELEASE && ui->dragging) {
1381 char data[256], *str;
1382
1383 /*
1384 * Terminate the drag, and if the piece has actually moved
1385 * then return a move string quoting the old and new
1386 * locations of the piece's anchor.
1387 */
1388 if (ui->drag_anchor != ui->drag_currpos) {
1389 sprintf(data, "M%d-%d", ui->drag_anchor, ui->drag_currpos);
1390 str = dupstr(data);
1391 } else
1392 str = MOVE_UI_UPDATE;
1393
1394 ui->dragging = false;
1395 ui->drag_anchor = ui->drag_currpos = -1;
1396 ui->drag_offset_x = ui->drag_offset_y = -1;
1397 memset(ui->reachable, 0, wh * sizeof(bool));
1398
1399 return str;
1400 } else if (button == ' ' && state->soln) {
1401 /*
1402 * Make the next move in the stored solution.
1403 */
1404 char data[256];
1405 int a1, a2;
1406
1407 a1 = state->soln->moves[state->soln_index*2];
1408 a2 = state->soln->moves[state->soln_index*2+1];
1409 if (a1 == state->lastmoved_pos)
1410 a1 = state->lastmoved;
1411
1412 sprintf(data, "M%d-%d", a1, a2);
1413 return dupstr(data);
1414 }
1415
1416 return NULL;
1417}
1418
1419static bool move_piece(int w, int h, const unsigned char *src,
1420 unsigned char *dst, bool *ff, int from, int to)
1421{
1422 int wh = w*h;
1423 int i, j;
1424
1425 if (!ISANCHOR(dst[from]))
1426 return false;
1427
1428 /*
1429 * Scan to the far end of the piece's linked list.
1430 */
1431 for (i = j = from; j < wh; j++)
1432 if (src[j] == DIST(j - i))
1433 i = j;
1434
1435 /*
1436 * Remove the piece from its old location in the new
1437 * game state.
1438 */
1439 for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1))
1440 dst[j] = EMPTY;
1441
1442 /*
1443 * And put it back in at the new location.
1444 */
1445 for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1)) {
1446 int jn = j + to - from;
1447 if (jn < 0 || jn >= wh)
1448 return false;
1449 if (dst[jn] == EMPTY && (!ff[jn] || src[from] == MAINANCHOR)) {
1450 dst[jn] = src[j];
1451 } else {
1452 return false;
1453 }
1454 }
1455
1456 return true;
1457}
1458
1459static game_state *execute_move(const game_state *state, const char *move)
1460{
1461 int w = state->w, h = state->h /* , wh = w*h */;
1462 char c;
1463 int a1, a2, n, movesize;
1464 game_state *ret = dup_game(state);
1465
1466 while (*move) {
1467 c = *move;
1468 if (c == 'S') {
1469 /*
1470 * This is a solve move, so we just set up a stored
1471 * solution path.
1472 */
1473 if (ret->soln && --ret->soln->refcount <= 0) {
1474 sfree(ret->soln->moves);
1475 sfree(ret->soln);
1476 }
1477 ret->soln = snew(struct game_solution);
1478 ret->soln->nmoves = 0;
1479 ret->soln->moves = NULL;
1480 ret->soln->refcount = 1;
1481 ret->soln_index = 0;
1482 ret->cheated = true;
1483
1484 movesize = 0;
1485 move++;
1486 while (1) {
1487 if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2) {
1488 free_game(ret);
1489 return NULL;
1490 }
1491
1492 /*
1493 * Special case: if the first move in the solution
1494 * involves the piece for which we already have a
1495 * partial stored move, adjust the source point to
1496 * the original starting point of that piece.
1497 */
1498 if (ret->soln->nmoves == 0 && a1 == ret->lastmoved)
1499 a1 = ret->lastmoved_pos;
1500
1501 if (ret->soln->nmoves >= movesize) {
1502 movesize = (ret->soln->nmoves + 48) * 4 / 3;
1503 ret->soln->moves = sresize(ret->soln->moves,
1504 2*movesize, int);
1505 }
1506
1507 ret->soln->moves[2*ret->soln->nmoves] = a1;
1508 ret->soln->moves[2*ret->soln->nmoves+1] = a2;
1509 ret->soln->nmoves++;
1510 move += n;
1511 if (*move != ',')
1512 break;
1513 move++; /* eat comma */
1514 }
1515 } else if (c == 'M') {
1516 move++;
1517 if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2 ||
1518 !move_piece(w, h, state->board, ret->board,
1519 state->imm->forcefield, a1, a2)) {
1520 free_game(ret);
1521 return NULL;
1522 }
1523 if (a1 == ret->lastmoved) {
1524 /*
1525 * If the player has moved the same piece as they
1526 * moved last time, don't increment the move
1527 * count. In fact, if they've put the piece back
1528 * where it started from, _decrement_ the move
1529 * count.
1530 */
1531 if (a2 == ret->lastmoved_pos) {
1532 ret->movecount--; /* reverted last move */
1533 ret->lastmoved = ret->lastmoved_pos = -1;
1534 } else {
1535 ret->lastmoved = a2;
1536 /* don't change lastmoved_pos */
1537 }
1538 } else {
1539 ret->lastmoved = a2;
1540 ret->lastmoved_pos = a1;
1541 ret->movecount++;
1542 }
1543
1544 /*
1545 * If we have a stored solution path, see if we've
1546 * strayed from it or successfully made the next move
1547 * along it.
1548 */
1549 if (ret->soln && ret->lastmoved_pos >= 0) {
1550 if (ret->lastmoved_pos !=
1551 ret->soln->moves[ret->soln_index*2]) {
1552 /* strayed from the path */
1553 ret->soln->refcount--;
1554 assert(ret->soln->refcount > 0);
1555 /* `state' at least still exists */
1556 ret->soln = NULL;
1557 ret->soln_index = -1;
1558 } else if (ret->lastmoved ==
1559 ret->soln->moves[ret->soln_index*2+1]) {
1560 /* advanced along the path */
1561 ret->soln_index++;
1562 if (ret->soln_index >= ret->soln->nmoves) {
1563 /* finished the path! */
1564 ret->soln->refcount--;
1565 assert(ret->soln->refcount > 0);
1566 /* `state' at least still exists */
1567 ret->soln = NULL;
1568 ret->soln_index = -1;
1569 }
1570 }
1571 }
1572
1573 if (ret->board[a2] == MAINANCHOR &&
1574 a2 == ret->ty * w + ret->tx && ret->completed < 0)
1575 ret->completed = ret->movecount;
1576 move += n;
1577 } else {
1578 free_game(ret);
1579 return NULL;
1580 }
1581 if (*move == ';')
1582 move++;
1583 else if (*move) {
1584 free_game(ret);
1585 return NULL;
1586 }
1587 }
1588
1589 return ret;
1590}
1591
1592/* ----------------------------------------------------------------------
1593 * Drawing routines.
1594 */
1595
1596static void game_compute_size(const game_params *params, int tilesize,
1597 const game_ui *ui, int *x, int *y)
1598{
1599 /* fool the macros */
1600 struct dummy { int tilesize; } dummy, *ds = &dummy;
1601 dummy.tilesize = tilesize;
1602
1603 *x = params->w * TILESIZE + 2*BORDER;
1604 *y = params->h * TILESIZE + 2*BORDER;
1605}
1606
1607static void game_set_size(drawing *dr, game_drawstate *ds,
1608 const game_params *params, int tilesize)
1609{
1610 ds->tilesize = tilesize;
1611}
1612
1613static void raise_colour(float *target, float *src, float *limit)
1614{
1615 int i;
1616 for (i = 0; i < 3; i++)
1617 target[i] = (2*src[i] + limit[i]) / 3;
1618}
1619
1620static float *game_colours(frontend *fe, int *ncolours)
1621{
1622 float *ret = snewn(3 * NCOLOURS, float);
1623
1624 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1625
1626 /*
1627 * When dragging a tile, we light it up a bit.
1628 */
1629 raise_colour(ret+3*COL_DRAGGING,
1630 ret+3*COL_BACKGROUND, ret+3*COL_HIGHLIGHT);
1631 raise_colour(ret+3*COL_DRAGGING_HIGHLIGHT,
1632 ret+3*COL_HIGHLIGHT, ret+3*COL_HIGHLIGHT);
1633 raise_colour(ret+3*COL_DRAGGING_LOWLIGHT,
1634 ret+3*COL_LOWLIGHT, ret+3*COL_HIGHLIGHT);
1635
1636 /*
1637 * The main tile is tinted blue.
1638 */
1639 ret[COL_MAIN * 3 + 0] = ret[COL_BACKGROUND * 3 + 0];
1640 ret[COL_MAIN * 3 + 1] = ret[COL_BACKGROUND * 3 + 1];
1641 ret[COL_MAIN * 3 + 2] = ret[COL_HIGHLIGHT * 3 + 2];
1642 game_mkhighlight_specific(fe, ret, COL_MAIN,
1643 COL_MAIN_HIGHLIGHT, COL_MAIN_LOWLIGHT);
1644
1645 /*
1646 * And we light that up a bit too when dragging.
1647 */
1648 raise_colour(ret+3*COL_MAIN_DRAGGING,
1649 ret+3*COL_MAIN, ret+3*COL_MAIN_HIGHLIGHT);
1650 raise_colour(ret+3*COL_MAIN_DRAGGING_HIGHLIGHT,
1651 ret+3*COL_MAIN_HIGHLIGHT, ret+3*COL_MAIN_HIGHLIGHT);
1652 raise_colour(ret+3*COL_MAIN_DRAGGING_LOWLIGHT,
1653 ret+3*COL_MAIN_LOWLIGHT, ret+3*COL_MAIN_HIGHLIGHT);
1654
1655 /*
1656 * The target area on the floor is tinted green.
1657 */
1658 ret[COL_TARGET * 3 + 0] = ret[COL_BACKGROUND * 3 + 0];
1659 ret[COL_TARGET * 3 + 1] = ret[COL_HIGHLIGHT * 3 + 1];
1660 ret[COL_TARGET * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
1661 game_mkhighlight_specific(fe, ret, COL_TARGET,
1662 COL_TARGET_HIGHLIGHT, COL_TARGET_LOWLIGHT);
1663
1664 *ncolours = NCOLOURS;
1665 return ret;
1666}
1667
1668static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1669{
1670 int w = state->w, h = state->h, wh = w*h;
1671 struct game_drawstate *ds = snew(struct game_drawstate);
1672 int i;
1673
1674 ds->tilesize = 0;
1675 ds->w = w;
1676 ds->h = h;
1677 ds->grid = snewn(wh, unsigned long);
1678 for (i = 0; i < wh; i++)
1679 ds->grid[i] = ~(unsigned long)0;
1680
1681 return ds;
1682}
1683
1684static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1685{
1686 sfree(ds->grid);
1687 sfree(ds);
1688}
1689
1690#define BG_NORMAL 0x00000001UL
1691#define BG_TARGET 0x00000002UL
1692#define BG_FORCEFIELD 0x00000004UL
1693#define FLASH_LOW 0x00000008UL
1694#define FLASH_HIGH 0x00000010UL
1695#define FG_WALL 0x00000020UL
1696#define FG_MAIN 0x00000040UL
1697#define FG_NORMAL 0x00000080UL
1698#define FG_DRAGGING 0x00000100UL
1699#define FG_SHADOW 0x00000200UL
1700#define FG_SOLVEPIECE 0x00000400UL
1701#define FG_MAINPIECESH 11
1702#define FG_SHADOWSH 19
1703
1704#define PIECE_LBORDER 0x00000001UL
1705#define PIECE_TBORDER 0x00000002UL
1706#define PIECE_RBORDER 0x00000004UL
1707#define PIECE_BBORDER 0x00000008UL
1708#define PIECE_TLCORNER 0x00000010UL
1709#define PIECE_TRCORNER 0x00000020UL
1710#define PIECE_BLCORNER 0x00000040UL
1711#define PIECE_BRCORNER 0x00000080UL
1712#define PIECE_MASK 0x000000FFUL
1713
1714/*
1715 * Utility function.
1716 */
1717#define TYPE_MASK 0xF000
1718#define COL_MASK 0x0FFF
1719#define TYPE_RECT 0x0000
1720#define TYPE_TLCIRC 0x4000
1721#define TYPE_TRCIRC 0x5000
1722#define TYPE_BLCIRC 0x6000
1723#define TYPE_BRCIRC 0x7000
1724static void maybe_rect(drawing *dr, int x, int y, int w, int h,
1725 int coltype, int col2)
1726{
1727 int colour = coltype & COL_MASK, type = coltype & TYPE_MASK;
1728
1729 if (colour > NCOLOURS)
1730 return;
1731 if (type == TYPE_RECT) {
1732 draw_rect(dr, x, y, w, h, colour);
1733 } else {
1734 int cx, cy, r;
1735
1736 clip(dr, x, y, w, h);
1737
1738 cx = x;
1739 cy = y;
1740 r = w-1;
1741 if (type & 0x1000)
1742 cx += r;
1743 if (type & 0x2000)
1744 cy += r;
1745
1746 if (col2 == -1 || col2 == coltype) {
1747 assert(w == h);
1748 draw_circle(dr, cx, cy, r, colour, colour);
1749 } else {
1750 /*
1751 * We aim to draw a quadrant of a circle in two
1752 * different colours. We do this using Bresenham's
1753 * algorithm directly, because the Puzzles drawing API
1754 * doesn't have a draw-sector primitive.
1755 */
1756 int bx, by, bd, bd2;
1757 int xm = (type & 0x1000 ? -1 : +1);
1758 int ym = (type & 0x2000 ? -1 : +1);
1759
1760 by = r;
1761 bx = 0;
1762 bd = 0;
1763 while (by >= bx) {
1764 /*
1765 * Plot the point.
1766 */
1767 {
1768 int x1 = cx+xm*bx, y1 = cy+ym*bx;
1769 int x2, y2;
1770
1771 x2 = cx+xm*by; y2 = y1;
1772 draw_rect(dr, min(x1,x2), min(y1,y2),
1773 abs(x1-x2)+1, abs(y1-y2)+1, colour);
1774 x2 = x1; y2 = cy+ym*by;
1775 draw_rect(dr, min(x1,x2), min(y1,y2),
1776 abs(x1-x2)+1, abs(y1-y2)+1, col2);
1777 }
1778
1779 bd += 2*bx + 1;
1780 bd2 = bd - (2*by - 1);
1781 if (abs(bd2) < abs(bd)) {
1782 bd = bd2;
1783 by--;
1784 }
1785 bx++;
1786 }
1787 }
1788
1789 unclip(dr);
1790 }
1791}
1792
1793static void draw_wallpart(drawing *dr, game_drawstate *ds,
1794 int tx, int ty, unsigned long val,
1795 int cl, int cc, int ch)
1796{
1797 int coords[6];
1798
1799 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc);
1800 if (val & PIECE_LBORDER)
1801 draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, TILESIZE,
1802 ch);
1803 if (val & PIECE_RBORDER)
1804 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1805 HIGHLIGHT_WIDTH, TILESIZE, cl);
1806 if (val & PIECE_TBORDER)
1807 draw_rect(dr, tx, ty, TILESIZE, HIGHLIGHT_WIDTH, ch);
1808 if (val & PIECE_BBORDER)
1809 draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1810 TILESIZE, HIGHLIGHT_WIDTH, cl);
1811 if (!((PIECE_BBORDER | PIECE_LBORDER) &~ val)) {
1812 draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1813 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1814 clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1815 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1816 coords[0] = tx - 1;
1817 coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1818 coords[2] = tx + HIGHLIGHT_WIDTH;
1819 coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1820 coords[4] = tx - 1;
1821 coords[5] = ty + TILESIZE;
1822 draw_polygon(dr, coords, 3, ch, ch);
1823 unclip(dr);
1824 } else if (val & PIECE_BLCORNER) {
1825 draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1826 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1827 clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1828 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1829 coords[0] = tx - 1;
1830 coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1831 coords[2] = tx + HIGHLIGHT_WIDTH;
1832 coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1833 coords[4] = tx - 1;
1834 coords[5] = ty + TILESIZE;
1835 draw_polygon(dr, coords, 3, cl, cl);
1836 unclip(dr);
1837 }
1838 if (!((PIECE_TBORDER | PIECE_RBORDER) &~ val)) {
1839 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1840 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1841 clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1842 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1843 coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1844 coords[1] = ty - 1;
1845 coords[2] = tx + TILESIZE;
1846 coords[3] = ty - 1;
1847 coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1848 coords[5] = ty + HIGHLIGHT_WIDTH;
1849 draw_polygon(dr, coords, 3, ch, ch);
1850 unclip(dr);
1851 } else if (val & PIECE_TRCORNER) {
1852 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1853 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1854 clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1855 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1856 coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1857 coords[1] = ty - 1;
1858 coords[2] = tx + TILESIZE;
1859 coords[3] = ty - 1;
1860 coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1861 coords[5] = ty + HIGHLIGHT_WIDTH;
1862 draw_polygon(dr, coords, 3, cl, cl);
1863 unclip(dr);
1864 }
1865 if (val & PIECE_TLCORNER)
1866 draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1867 if (val & PIECE_BRCORNER)
1868 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH,
1869 ty+TILESIZE-HIGHLIGHT_WIDTH,
1870 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1871}
1872
1873static void draw_piecepart(drawing *dr, game_drawstate *ds,
1874 int tx, int ty, unsigned long val,
1875 int cl, int cc, int ch)
1876{
1877 int x[6], y[6];
1878
1879 /*
1880 * Drawing the blocks is hellishly fiddly. The blocks don't
1881 * stretch to the full size of the tile; there's a border
1882 * around them of size BORDER_WIDTH. Then they have bevelled
1883 * borders of size HIGHLIGHT_WIDTH, and also rounded corners.
1884 *
1885 * I tried for some time to find a clean and clever way to
1886 * figure out what needed drawing from the corner and border
1887 * flags, but in the end the cleanest way I could find was the
1888 * following. We divide the grid square into 25 parts by
1889 * ruling four horizontal and four vertical lines across it;
1890 * those lines are at BORDER_WIDTH and BORDER_WIDTH +
1891 * HIGHLIGHT_WIDTH from the top, from the bottom, from the
1892 * left and from the right. Then we carefully consider each of
1893 * the resulting 25 sections of square, and decide separately
1894 * what needs to go in it based on the flags. In complicated
1895 * cases there can be up to five possibilities affecting any
1896 * given section (no corner or border flags, just the corner
1897 * flag, one border flag, the other border flag, both border
1898 * flags). So there's a lot of very fiddly logic here and all
1899 * I could really think to do was give it my best shot and
1900 * then test it and correct all the typos. Not fun to write,
1901 * and I'm sure it isn't fun to read either, but it seems to
1902 * work.
1903 */
1904
1905 x[0] = tx;
1906 x[1] = x[0] + BORDER_WIDTH;
1907 x[2] = x[1] + HIGHLIGHT_WIDTH;
1908 x[5] = tx + TILESIZE;
1909 x[4] = x[5] - BORDER_WIDTH;
1910 x[3] = x[4] - HIGHLIGHT_WIDTH;
1911
1912 y[0] = ty;
1913 y[1] = y[0] + BORDER_WIDTH;
1914 y[2] = y[1] + HIGHLIGHT_WIDTH;
1915 y[5] = ty + TILESIZE;
1916 y[4] = y[5] - BORDER_WIDTH;
1917 y[3] = y[4] - HIGHLIGHT_WIDTH;
1918
1919#define RECT(p,q) x[p], y[q], x[(p)+1]-x[p], y[(q)+1]-y[q]
1920
1921 maybe_rect(dr, RECT(0,0),
1922 (val & (PIECE_TLCORNER | PIECE_TBORDER |
1923 PIECE_LBORDER)) ? -1 : cc, -1);
1924 maybe_rect(dr, RECT(1,0),
1925 (val & PIECE_TLCORNER) ? ch : (val & PIECE_TBORDER) ? -1 :
1926 (val & PIECE_LBORDER) ? ch : cc, -1);
1927 maybe_rect(dr, RECT(2,0),
1928 (val & PIECE_TBORDER) ? -1 : cc, -1);
1929 maybe_rect(dr, RECT(3,0),
1930 (val & PIECE_TRCORNER) ? cl : (val & PIECE_TBORDER) ? -1 :
1931 (val & PIECE_RBORDER) ? cl : cc, -1);
1932 maybe_rect(dr, RECT(4,0),
1933 (val & (PIECE_TRCORNER | PIECE_TBORDER |
1934 PIECE_RBORDER)) ? -1 : cc, -1);
1935 maybe_rect(dr, RECT(0,1),
1936 (val & PIECE_TLCORNER) ? ch : (val & PIECE_LBORDER) ? -1 :
1937 (val & PIECE_TBORDER) ? ch : cc, -1);
1938 maybe_rect(dr, RECT(1,1),
1939 (val & PIECE_TLCORNER) ? cc : -1, -1);
1940 maybe_rect(dr, RECT(1,1),
1941 (val & PIECE_TLCORNER) ? ch | TYPE_TLCIRC :
1942 !((PIECE_TBORDER | PIECE_LBORDER) &~ val) ? ch | TYPE_BRCIRC :
1943 (val & (PIECE_TBORDER | PIECE_LBORDER)) ? ch : cc, -1);
1944 maybe_rect(dr, RECT(2,1),
1945 (val & PIECE_TBORDER) ? ch : cc, -1);
1946 maybe_rect(dr, RECT(3,1),
1947 (val & PIECE_TRCORNER) ? cc : -1, -1);
1948 maybe_rect(dr, RECT(3,1),
1949 (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_TBORDER ? ch :
1950 (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_RBORDER ? cl :
1951 !((PIECE_TBORDER|PIECE_RBORDER) &~ val) ? cl | TYPE_BLCIRC :
1952 (val & PIECE_TRCORNER) ? cl | TYPE_TRCIRC :
1953 cc, ch);
1954 maybe_rect(dr, RECT(4,1),
1955 (val & PIECE_TRCORNER) ? ch : (val & PIECE_RBORDER) ? -1 :
1956 (val & PIECE_TBORDER) ? ch : cc, -1);
1957 maybe_rect(dr, RECT(0,2),
1958 (val & PIECE_LBORDER) ? -1 : cc, -1);
1959 maybe_rect(dr, RECT(1,2),
1960 (val & PIECE_LBORDER) ? ch : cc, -1);
1961 maybe_rect(dr, RECT(2,2),
1962 cc, -1);
1963 maybe_rect(dr, RECT(3,2),
1964 (val & PIECE_RBORDER) ? cl : cc, -1);
1965 maybe_rect(dr, RECT(4,2),
1966 (val & PIECE_RBORDER) ? -1 : cc, -1);
1967 maybe_rect(dr, RECT(0,3),
1968 (val & PIECE_BLCORNER) ? cl : (val & PIECE_LBORDER) ? -1 :
1969 (val & PIECE_BBORDER) ? cl : cc, -1);
1970 maybe_rect(dr, RECT(1,3),
1971 (val & PIECE_BLCORNER) ? cc : -1, -1);
1972 maybe_rect(dr, RECT(1,3),
1973 (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_BBORDER ? cl :
1974 (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_LBORDER ? ch :
1975 !((PIECE_BBORDER|PIECE_LBORDER) &~ val) ? ch | TYPE_TRCIRC :
1976 (val & PIECE_BLCORNER) ? ch | TYPE_BLCIRC :
1977 cc, cl);
1978 maybe_rect(dr, RECT(2,3),
1979 (val & PIECE_BBORDER) ? cl : cc, -1);
1980 maybe_rect(dr, RECT(3,3),
1981 (val & PIECE_BRCORNER) ? cc : -1, -1);
1982 maybe_rect(dr, RECT(3,3),
1983 (val & PIECE_BRCORNER) ? cl | TYPE_BRCIRC :
1984 !((PIECE_BBORDER | PIECE_RBORDER) &~ val) ? cl | TYPE_TLCIRC :
1985 (val & (PIECE_BBORDER | PIECE_RBORDER)) ? cl : cc, -1);
1986 maybe_rect(dr, RECT(4,3),
1987 (val & PIECE_BRCORNER) ? cl : (val & PIECE_RBORDER) ? -1 :
1988 (val & PIECE_BBORDER) ? cl : cc, -1);
1989 maybe_rect(dr, RECT(0,4),
1990 (val & (PIECE_BLCORNER | PIECE_BBORDER |
1991 PIECE_LBORDER)) ? -1 : cc, -1);
1992 maybe_rect(dr, RECT(1,4),
1993 (val & PIECE_BLCORNER) ? ch : (val & PIECE_BBORDER) ? -1 :
1994 (val & PIECE_LBORDER) ? ch : cc, -1);
1995 maybe_rect(dr, RECT(2,4),
1996 (val & PIECE_BBORDER) ? -1 : cc, -1);
1997 maybe_rect(dr, RECT(3,4),
1998 (val & PIECE_BRCORNER) ? cl : (val & PIECE_BBORDER) ? -1 :
1999 (val & PIECE_RBORDER) ? cl : cc, -1);
2000 maybe_rect(dr, RECT(4,4),
2001 (val & (PIECE_BRCORNER | PIECE_BBORDER |
2002 PIECE_RBORDER)) ? -1 : cc, -1);
2003
2004#undef RECT
2005}
2006
2007static void draw_tile(drawing *dr, game_drawstate *ds,
2008 int x, int y, unsigned long val)
2009{
2010 int tx = COORD(x), ty = COORD(y);
2011 int cc, ch, cl;
2012
2013 /*
2014 * Draw the tile background.
2015 */
2016 if (val & BG_TARGET)
2017 cc = COL_TARGET;
2018 else
2019 cc = COL_BACKGROUND;
2020 ch = cc+1;
2021 cl = cc+2;
2022 if (val & FLASH_LOW)
2023 cc = cl;
2024 else if (val & FLASH_HIGH)
2025 cc = ch;
2026
2027 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc);
2028 if (val & BG_FORCEFIELD) {
2029 /*
2030 * Cattle-grid effect to indicate that nothing but the
2031 * main block can slide over this square.
2032 */
2033 int n = 3 * (TILESIZE / (3*HIGHLIGHT_WIDTH));
2034 int i;
2035
2036 for (i = 1; i < n; i += 3) {
2037 draw_rect(dr, tx,ty+(TILESIZE*i/n), TILESIZE,HIGHLIGHT_WIDTH, cl);
2038 draw_rect(dr, tx+(TILESIZE*i/n),ty, HIGHLIGHT_WIDTH,TILESIZE, cl);
2039 }
2040 }
2041
2042 /*
2043 * Draw the tile midground: a shadow of a block, for
2044 * displaying partial solutions.
2045 */
2046 if (val & FG_SHADOW) {
2047 draw_piecepart(dr, ds, tx, ty, (val >> FG_SHADOWSH) & PIECE_MASK,
2048 cl, cl, cl);
2049 }
2050
2051 /*
2052 * Draw the tile foreground, i.e. some section of a block or
2053 * wall.
2054 */
2055 if (val & FG_WALL) {
2056 cc = COL_BACKGROUND;
2057 ch = cc+1;
2058 cl = cc+2;
2059 if (val & FLASH_LOW)
2060 cc = cl;
2061 else if (val & FLASH_HIGH)
2062 cc = ch;
2063
2064 draw_wallpart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK,
2065 cl, cc, ch);
2066 } else if (val & (FG_MAIN | FG_NORMAL)) {
2067 if (val & FG_DRAGGING)
2068 cc = (val & FG_MAIN ? COL_MAIN_DRAGGING : COL_DRAGGING);
2069 else
2070 cc = (val & FG_MAIN ? COL_MAIN : COL_BACKGROUND);
2071 ch = cc+1;
2072 cl = cc+2;
2073
2074 if (val & FLASH_LOW)
2075 cc = cl;
2076 else if (val & (FLASH_HIGH | FG_SOLVEPIECE))
2077 cc = ch;
2078
2079 draw_piecepart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK,
2080 cl, cc, ch);
2081 }
2082
2083 draw_update(dr, tx, ty, TILESIZE, TILESIZE);
2084}
2085
2086static unsigned long find_piecepart(int w, int h, DSF *dsf, int x, int y)
2087{
2088 int i = y*w+x;
2089 int canon = dsf_canonify(dsf, i);
2090 unsigned long val = 0;
2091
2092 if (x == 0 || canon != dsf_canonify(dsf, i-1))
2093 val |= PIECE_LBORDER;
2094 if (y== 0 || canon != dsf_canonify(dsf, i-w))
2095 val |= PIECE_TBORDER;
2096 if (x == w-1 || canon != dsf_canonify(dsf, i+1))
2097 val |= PIECE_RBORDER;
2098 if (y == h-1 || canon != dsf_canonify(dsf, i+w))
2099 val |= PIECE_BBORDER;
2100 if (!(val & (PIECE_TBORDER | PIECE_LBORDER)) &&
2101 canon != dsf_canonify(dsf, i-1-w))
2102 val |= PIECE_TLCORNER;
2103 if (!(val & (PIECE_TBORDER | PIECE_RBORDER)) &&
2104 canon != dsf_canonify(dsf, i+1-w))
2105 val |= PIECE_TRCORNER;
2106 if (!(val & (PIECE_BBORDER | PIECE_LBORDER)) &&
2107 canon != dsf_canonify(dsf, i-1+w))
2108 val |= PIECE_BLCORNER;
2109 if (!(val & (PIECE_BBORDER | PIECE_RBORDER)) &&
2110 canon != dsf_canonify(dsf, i+1+w))
2111 val |= PIECE_BRCORNER;
2112 return val;
2113}
2114
2115static void game_redraw(drawing *dr, game_drawstate *ds,
2116 const game_state *oldstate, const game_state *state,
2117 int dir, const game_ui *ui,
2118 float animtime, float flashtime)
2119{
2120 int w = state->w, h = state->h, wh = w*h;
2121 unsigned char *board;
2122 DSF *dsf;
2123 int x, y, mainanchor, mainpos, dragpos, solvepos, solvesrc, solvedst;
2124
2125 /*
2126 * Construct the board we'll be displaying (which may be
2127 * different from the one in state if ui describes a drag in
2128 * progress).
2129 */
2130 board = snewn(wh, unsigned char);
2131 memcpy(board, state->board, wh);
2132 if (ui->dragging) {
2133 bool mpret = move_piece(w, h, state->board, board,
2134 state->imm->forcefield,
2135 ui->drag_anchor, ui->drag_currpos);
2136 assert(mpret);
2137 }
2138
2139 if (state->soln) {
2140 solvesrc = state->soln->moves[state->soln_index*2];
2141 solvedst = state->soln->moves[state->soln_index*2+1];
2142 if (solvesrc == state->lastmoved_pos)
2143 solvesrc = state->lastmoved;
2144 if (solvesrc == ui->drag_anchor)
2145 solvesrc = ui->drag_currpos;
2146 } else
2147 solvesrc = solvedst = -1;
2148
2149 /*
2150 * Build a dsf out of that board, so we can conveniently tell
2151 * which edges are connected and which aren't.
2152 */
2153 dsf = dsf_new(wh);
2154 mainanchor = -1;
2155 for (y = 0; y < h; y++)
2156 for (x = 0; x < w; x++) {
2157 int i = y*w+x;
2158
2159 if (ISDIST(board[i]))
2160 dsf_merge(dsf, i, i - board[i]);
2161 if (board[i] == MAINANCHOR)
2162 mainanchor = i;
2163 if (board[i] == WALL) {
2164 if (x > 0 && board[i-1] == WALL)
2165 dsf_merge(dsf, i, i-1);
2166 if (y > 0 && board[i-w] == WALL)
2167 dsf_merge(dsf, i, i-w);
2168 }
2169 }
2170 assert(mainanchor >= 0);
2171 mainpos = dsf_canonify(dsf, mainanchor);
2172 dragpos = ui->drag_currpos > 0 ? dsf_canonify(dsf, ui->drag_currpos) : -1;
2173 solvepos = solvesrc >= 0 ? dsf_canonify(dsf, solvesrc) : -1;
2174
2175 /*
2176 * Now we can construct the data about what we want to draw.
2177 */
2178 for (y = 0; y < h; y++)
2179 for (x = 0; x < w; x++) {
2180 int i = y*w+x;
2181 int j;
2182 unsigned long val;
2183 int canon;
2184
2185 /*
2186 * See if this square is part of the target area.
2187 */
2188 j = i + mainanchor - (state->ty * w + state->tx);
2189 while (j >= 0 && j < wh && ISDIST(board[j]))
2190 j -= board[j];
2191 if (j == mainanchor)
2192 val = BG_TARGET;
2193 else
2194 val = BG_NORMAL;
2195
2196 if (state->imm->forcefield[i])
2197 val |= BG_FORCEFIELD;
2198
2199 if (flashtime > 0) {
2200 int flashtype = (int)(flashtime / FLASH_INTERVAL) & 1;
2201 val |= (flashtype ? FLASH_LOW : FLASH_HIGH);
2202 }
2203
2204 if (board[i] != EMPTY) {
2205 canon = dsf_canonify(dsf, i);
2206
2207 if (board[i] == WALL)
2208 val |= FG_WALL;
2209 else if (canon == mainpos)
2210 val |= FG_MAIN;
2211 else
2212 val |= FG_NORMAL;
2213 if (canon == dragpos)
2214 val |= FG_DRAGGING;
2215 if (canon == solvepos)
2216 val |= FG_SOLVEPIECE;
2217
2218 /*
2219 * Now look around to see if other squares
2220 * belonging to the same block are adjacent to us.
2221 */
2222 val |= find_piecepart(w, h, dsf, x, y) << FG_MAINPIECESH;
2223 }
2224
2225 /*
2226 * If we're in the middle of showing a solution,
2227 * display a shadow piece for the target of the
2228 * current move.
2229 */
2230 if (solvepos >= 0) {
2231 int si = i - solvedst + solvesrc;
2232 if (si >= 0 && si < wh && dsf_canonify(dsf, si) == solvepos) {
2233 val |= find_piecepart(w, h, dsf,
2234 si % w, si / w) << FG_SHADOWSH;
2235 val |= FG_SHADOW;
2236 }
2237 }
2238
2239 if (val != ds->grid[i]) {
2240 draw_tile(dr, ds, x, y, val);
2241 ds->grid[i] = val;
2242 }
2243 }
2244
2245 /*
2246 * Update the status bar.
2247 */
2248 {
2249 char statusbuf[256];
2250
2251 sprintf(statusbuf, "%sMoves: %d",
2252 (state->completed >= 0 ?
2253 (state->cheated ? "Auto-solved. " : "COMPLETED! ") :
2254 (state->cheated ? "Auto-solver used. " : "")),
2255 (state->completed >= 0 ? state->completed : state->movecount));
2256 if (state->minmoves >= 0)
2257 sprintf(statusbuf+strlen(statusbuf), " (min %d)",
2258 state->minmoves);
2259
2260 status_bar(dr, statusbuf);
2261 }
2262
2263 dsf_free(dsf);
2264 sfree(board);
2265}
2266
2267static float game_anim_length(const game_state *oldstate,
2268 const game_state *newstate, int dir, game_ui *ui)
2269{
2270 return 0.0F;
2271}
2272
2273static float game_flash_length(const game_state *oldstate,
2274 const game_state *newstate, int dir, game_ui *ui)
2275{
2276 if (oldstate->completed < 0 && newstate->completed >= 0)
2277 return FLASH_TIME;
2278
2279 return 0.0F;
2280}
2281
2282static void game_get_cursor_location(const game_ui *ui,
2283 const game_drawstate *ds,
2284 const game_state *state,
2285 const game_params *params,
2286 int *x, int *y, int *w, int *h)
2287{
2288}
2289
2290static int game_status(const game_state *state)
2291{
2292 return state->completed ? +1 : 0;
2293}
2294
2295static bool game_timing_state(const game_state *state, game_ui *ui)
2296{
2297 return true;
2298}
2299
2300static void game_print_size(const game_params *params, const game_ui *ui,
2301 float *x, float *y)
2302{
2303}
2304
2305static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2306 int tilesize)
2307{
2308}
2309
2310#ifdef COMBINED
2311#define thegame slide
2312#endif
2313
2314const struct game thegame = {
2315 "Slide", NULL, NULL,
2316 default_params,
2317 game_fetch_preset, NULL,
2318 decode_params,
2319 encode_params,
2320 free_params,
2321 dup_params,
2322 true, game_configure, custom_params,
2323 validate_params,
2324 new_game_desc,
2325 validate_desc,
2326 new_game,
2327 dup_game,
2328 free_game,
2329 true, solve_game,
2330 true, game_can_format_as_text_now, game_text_format,
2331 NULL, NULL, /* get_prefs, set_prefs */
2332 new_ui,
2333 free_ui,
2334 NULL, /* encode_ui */
2335 NULL, /* decode_ui */
2336 NULL, /* game_request_keys */
2337 game_changed_state,
2338 NULL, /* current_key_label */
2339 interpret_move,
2340 execute_move,
2341 PREFERRED_TILESIZE, game_compute_size, game_set_size,
2342 game_colours,
2343 game_new_drawstate,
2344 game_free_drawstate,
2345 game_redraw,
2346 game_anim_length,
2347 game_flash_length,
2348 game_get_cursor_location,
2349 game_status,
2350 false, false, game_print_size, game_print,
2351 true, /* wants_statusbar */
2352 false, game_timing_state,
2353 0, /* flags */
2354};
2355
2356#ifdef STANDALONE_SOLVER
2357
2358#include <stdarg.h>
2359
2360int main(int argc, char **argv)
2361{
2362 game_params *p;
2363 game_state *s;
2364 char *id = NULL, *desc;
2365 const char *err;
2366 bool count = false;
2367 int ret;
2368 int *moves;
2369
2370 while (--argc > 0) {
2371 char *p = *++argv;
2372 /*
2373 if (!strcmp(p, "-v")) {
2374 verbose = true;
2375 } else
2376 */
2377 if (!strcmp(p, "-c")) {
2378 count = true;
2379 } else if (*p == '-') {
2380 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2381 return 1;
2382 } else {
2383 id = p;
2384 }
2385 }
2386
2387 if (!id) {
2388 fprintf(stderr, "usage: %s [-c | -v] <game_id>\n", argv[0]);
2389 return 1;
2390 }
2391
2392 desc = strchr(id, ':');
2393 if (!desc) {
2394 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2395 return 1;
2396 }
2397 *desc++ = '\0';
2398
2399 p = default_params();
2400 decode_params(p, id);
2401 err = validate_desc(p, desc);
2402 if (err) {
2403 fprintf(stderr, "%s: %s\n", argv[0], err);
2404 return 1;
2405 }
2406 s = new_game(NULL, p, desc);
2407
2408 ret = solve_board(s->w, s->h, s->board, s->imm->forcefield,
2409 s->tx, s->ty, -1, &moves);
2410 if (ret < 0) {
2411 printf("No solution found\n");
2412 } else {
2413 int index = 0;
2414 if (count) {
2415 printf("%d moves required\n", ret);
2416 return 0;
2417 }
2418 while (1) {
2419 bool moveret;
2420 char *text = board_text_format(s->w, s->h, s->board,
2421 s->imm->forcefield);
2422 game_state *s2;
2423
2424 printf("position %d:\n%s", index, text);
2425
2426 if (index >= ret)
2427 break;
2428
2429 s2 = dup_game(s);
2430 moveret = move_piece(s->w, s->h, s->board,
2431 s2->board, s->imm->forcefield,
2432 moves[index*2], moves[index*2+1]);
2433 assert(moveret);
2434
2435 free_game(s);
2436 s = s2;
2437 index++;
2438 }
2439 }
2440
2441 return 0;
2442}
2443
2444#endif
diff --git a/apps/plugins/puzzles/src/unfinished/sokoban.c b/apps/plugins/puzzles/src/unfinished/sokoban.c
new file mode 100644
index 0000000000..1f3c688af1
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/sokoban.c
@@ -0,0 +1,1476 @@
1/*
2 * sokoban.c: An implementation of the well-known Sokoban barrel-
3 * pushing game. Random generation is too simplistic to be
4 * credible, but the rest of the gameplay works well enough to use
5 * it with hand-written level descriptions.
6 */
7
8/*
9 * TODO:
10 *
11 * - I think it would be better to ditch the `prev' array, and
12 * instead make the `dist' array strictly monotonic (by having
13 * each distance be something like I*A+S, where A is the grid
14 * area, I the number of INITIAL squares trampled on, and S the
15 * number of harmless spaces moved through). This would permit
16 * the path-tracing when a pull is actually made to choose
17 * randomly from all the possible shortest routes, which would
18 * be superior in terms of eliminating directional bias.
19 * + So when tracing the path back to the current px,py, we
20 * look at all four adjacent squares, find the minimum
21 * distance, check that it's _strictly smaller_ than that of
22 * the current square, and restrict our choice to precisely
23 * those squares with that minimum distance.
24 * + The other place `prev' is currently used is in the check
25 * for consistency of a pull. We would have to replace the
26 * check for whether prev[ny*w+nx]==oy*w+ox with a check that
27 * made sure there was at least one adjacent square with a
28 * smaller distance which _wasn't_ oy*w+ox. Then when we did
29 * the path-tracing we'd also have to take this special case
30 * into account.
31 *
32 * - More discriminating choice of pull. (Snigger.)
33 * + favour putting targets in clumps
34 * + try to shoot for a reasonably consistent number of barrels
35 * (adjust willingness to generate a new barrel depending on
36 * how many are already present)
37 * + adjust willingness to break new ground depending on how
38 * much is already broken
39 *
40 * - generation time parameters:
41 * + enable NetHack mode (and find a better place for the hole)
42 * + decide how many of the remaining Is should be walls
43 *
44 * - at the end of generation, randomly position the starting
45 * player coordinates, probably by (somehow) reusing the same
46 * bfs currently inside the loop.
47 *
48 * - possible backtracking?
49 *
50 * - IWBNI we could spot completely unreachable bits of level at
51 * the outside, and not bother drawing grid lines for them. The
52 * NH levels currently look a bit weird with grid lines on the
53 * outside of the boundary.
54 */
55
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include <assert.h>
60#include <ctype.h>
61#ifdef NO_TGMATH_H
62# include <math.h>
63#else
64# include <tgmath.h>
65#endif
66
67#include "puzzles.h"
68
69/*
70 * Various subsets of these constants are used during game
71 * generation, game play, game IDs and the game_drawstate.
72 */
73#define INITIAL 'i' /* used only in game generation */
74#define SPACE 's'
75#define WALL 'w'
76#define PIT 'p'
77#define DEEP_PIT 'd'
78#define TARGET 't'
79#define BARREL 'b'
80#define BARRELTARGET 'f' /* target is 'f'illed */
81#define PLAYER 'u' /* yo'u'; used in game IDs */
82#define PLAYERTARGET 'v' /* bad letter: v is to u as t is to s */
83#define INVALID '!' /* used in drawstate to force redraw */
84/*
85 * We also support the use of any capital letter as a barrel, which
86 * will be displayed with that letter as a label. (This facilitates
87 * people distributing annotated game IDs for particular Sokoban
88 * levels, so they can accompany them with verbal instructions
89 * about pushing particular barrels in particular ways.) Therefore,
90 * to find out whether something is a barrel, we need a test
91 * function which does a bit more than just comparing to BARREL.
92 *
93 * When resting on target squares, capital-letter barrels are
94 * replaced with their control-character value (A -> ^A).
95 */
96#define IS_PLAYER(c) ( (c)==PLAYER || (c)==PLAYERTARGET )
97#define IS_BARREL(c) ( (c)==BARREL || (c)==BARRELTARGET || \
98 ((c)>='A' && (c)<='Z') || ((c)>=1 && (c)<=26) )
99#define IS_ON_TARGET(c) ( (c)==TARGET || (c)==BARRELTARGET || \
100 (c)==PLAYERTARGET || ((c)>=1 && (c)<=26) )
101#define TARGETISE(b) ( (b)==BARREL ? BARRELTARGET : (b)-('A'-1) )
102#define DETARGETISE(b) ( (b)==BARRELTARGET ? BARREL : (b)+('A'-1) )
103#define BARREL_LABEL(b) ( (b)>='A'&&(b)<='Z' ? (b) : \
104 (b)>=1 && (b)<=26 ? (b)+('A'-1) : 0 )
105
106#define DX(d) (d == 0 ? -1 : d == 2 ? +1 : 0)
107#define DY(d) (d == 1 ? -1 : d == 3 ? +1 : 0)
108
109#define FLASH_LENGTH 0.3F
110
111enum {
112 COL_BACKGROUND,
113 COL_TARGET,
114 COL_PIT,
115 COL_DEEP_PIT,
116 COL_BARREL,
117 COL_PLAYER,
118 COL_TEXT,
119 COL_GRID,
120 COL_OUTLINE,
121 COL_HIGHLIGHT,
122 COL_LOWLIGHT,
123 COL_WALL,
124 NCOLOURS
125};
126
127struct game_params {
128 int w, h;
129 /*
130 * FIXME: a parameter involving degree of filling in?
131 */
132};
133
134struct game_state {
135 game_params p;
136 unsigned char *grid;
137 int px, py;
138 bool completed;
139};
140
141static game_params *default_params(void)
142{
143 game_params *ret = snew(game_params);
144
145 ret->w = 12;
146 ret->h = 10;
147
148 return ret;
149}
150
151static void free_params(game_params *params)
152{
153 sfree(params);
154}
155
156static game_params *dup_params(const game_params *params)
157{
158 game_params *ret = snew(game_params);
159 *ret = *params; /* structure copy */
160 return ret;
161}
162
163static const struct game_params sokoban_presets[] = {
164 { 12, 10 },
165 { 16, 12 },
166 { 20, 16 },
167};
168
169static bool game_fetch_preset(int i, char **name, game_params **params)
170{
171 game_params p, *ret;
172 char *retname;
173 char namebuf[80];
174
175 if (i < 0 || i >= lenof(sokoban_presets))
176 return false;
177
178 p = sokoban_presets[i];
179 ret = dup_params(&p);
180 sprintf(namebuf, "%dx%d", ret->w, ret->h);
181 retname = dupstr(namebuf);
182
183 *params = ret;
184 *name = retname;
185 return true;
186}
187
188static void decode_params(game_params *params, char const *string)
189{
190 params->w = params->h = atoi(string);
191 while (*string && isdigit((unsigned char)*string)) string++;
192 if (*string == 'x') {
193 string++;
194 params->h = atoi(string);
195 }
196}
197
198static char *encode_params(const game_params *params, bool full)
199{
200 char data[256];
201
202 sprintf(data, "%dx%d", params->w, params->h);
203
204 return dupstr(data);
205}
206
207static config_item *game_configure(const game_params *params)
208{
209 config_item *ret;
210 char buf[80];
211
212 ret = snewn(3, config_item);
213
214 ret[0].name = "Width";
215 ret[0].type = C_STRING;
216 sprintf(buf, "%d", params->w);
217 ret[0].u.string.sval = dupstr(buf);
218
219 ret[1].name = "Height";
220 ret[1].type = C_STRING;
221 sprintf(buf, "%d", params->h);
222 ret[1].u.string.sval = dupstr(buf);
223
224 ret[2].name = NULL;
225 ret[2].type = C_END;
226
227 return ret;
228}
229
230static game_params *custom_params(const config_item *cfg)
231{
232 game_params *ret = snew(game_params);
233
234 ret->w = atoi(cfg[0].u.string.sval);
235 ret->h = atoi(cfg[1].u.string.sval);
236
237 return ret;
238}
239
240static const char *validate_params(const game_params *params, bool full)
241{
242 if (params->w < 4 || params->h < 4)
243 return "Width and height must both be at least 4";
244
245 return NULL;
246}
247
248/* ----------------------------------------------------------------------
249 * Game generation mechanism.
250 *
251 * To generate a Sokoban level, we begin with a completely blank
252 * grid and make valid inverse moves. Grid squares can be in a
253 * number of states. The states are:
254 *
255 * - INITIAL: this square has not as yet been touched by any
256 * inverse move, which essentially means we haven't decided what
257 * it is yet.
258 *
259 * - SPACE: this square is a space.
260 *
261 * - TARGET: this square is a space which is also the target for a
262 * barrel.
263 *
264 * - BARREL: this square contains a barrel.
265 *
266 * - BARRELTARGET: this square contains a barrel _on_ a target.
267 *
268 * - WALL: this square is a wall.
269 *
270 * - PLAYER: this square contains the player.
271 *
272 * - PLAYERTARGET: this square contains the player on a target.
273 *
274 * We begin with every square of the in state INITIAL, apart from a
275 * solid ring of WALLs around the edge. We randomly position the
276 * PLAYER somewhere. Thereafter our valid moves are:
277 *
278 * - to move the PLAYER in one direction _pulling_ a barrel after
279 * us. For this to work, we must have SPACE or INITIAL in the
280 * direction we're moving, and BARREL or BARRELTARGET in the
281 * direction we're moving away from. We leave SPACE or TARGET
282 * respectively in the vacated square.
283 *
284 * - to create a new barrel by transforming an INITIAL square into
285 * BARRELTARGET.
286 *
287 * - to move the PLAYER freely through SPACE and TARGET squares,
288 * leaving SPACE or TARGET where it started.
289 *
290 * - to move the player through INITIAL squares, carving a tunnel
291 * of SPACEs as it goes.
292 *
293 * We try to avoid destroying INITIAL squares wherever possible (if
294 * there's a path to where we want to be using only SPACE, then we
295 * should always use that). At the end of generation, every square
296 * still in state INITIAL is one which was not required at any
297 * point during generation, which means we can randomly choose
298 * whether to make it SPACE or WALL.
299 *
300 * It's unclear as yet what the right strategy for wall placement
301 * should be. Too few WALLs will yield many alternative solutions
302 * to the puzzle, whereas too many might rule out so many
303 * possibilities that the intended solution becomes obvious.
304 */
305
306static void sokoban_generate(int w, int h, unsigned char *grid, int moves,
307 bool nethack, random_state *rs)
308{
309 struct pull {
310 int ox, oy, nx, ny, score;
311 };
312
313 struct pull *pulls;
314 int *dist, *prev, *heap;
315 int x, y, px, py, i, j, d, heapsize, npulls;
316
317 pulls = snewn(w * h * 4, struct pull);
318 dist = snewn(w * h, int);
319 prev = snewn(w * h, int);
320 heap = snewn(w * h, int);
321
322 /*
323 * Configure the initial grid.
324 */
325 for (y = 0; y < h; y++)
326 for (x = 0; x < w; x++)
327 grid[y*w+x] = (x == 0 || y == 0 || x == w-1 || y == h-1 ?
328 WALL : INITIAL);
329 if (nethack)
330 grid[1] = DEEP_PIT;
331
332 /*
333 * Place the player.
334 */
335 i = random_upto(rs, (w-2) * (h-2));
336 x = 1 + i % (w-2);
337 y = 1 + i / (w-2);
338 grid[y*w+x] = SPACE;
339 px = x;
340 py = y;
341
342 /*
343 * Now loop around making random inverse Sokoban moves. In this
344 * loop we aim to make one actual barrel-pull per iteration,
345 * plus as many free moves as are necessary to get into
346 * position for that pull.
347 */
348 while (moves-- >= 0) {
349 /*
350 * First enumerate all the viable barrel-pulls we can
351 * possibly make, counting two pulls of the same barrel in
352 * different directions as different. We also include pulls
353 * we can perform by creating a new barrel. Each pull is
354 * marked with the amount of violence it would have to do
355 * to the grid.
356 */
357 npulls = 0;
358 for (y = 0; y < h; y++)
359 for (x = 0; x < w; x++)
360 for (d = 0; d < 4; d++) {
361 int dx = DX(d);
362 int dy = DY(d);
363 int nx = x + dx, ny = y + dy;
364 int npx = nx + dx, npy = ny + dy;
365 int score = 0;
366
367 /*
368 * The candidate move is to put the player at
369 * (nx,ny), and move him to (npx,npy), pulling
370 * a barrel at (x,y) to (nx,ny). So first we
371 * must check that all those squares are within
372 * the boundaries of the grid. For this it is
373 * sufficient to check npx,npy.
374 */
375 if (npx < 0 || npx >= w || npy < 0 || npy >= h)
376 continue;
377
378 /*
379 * (x,y) must either be a barrel, or a square
380 * which we can convert into a barrel.
381 */
382 switch (grid[y*w+x]) {
383 case BARREL: case BARRELTARGET:
384 break;
385 case INITIAL:
386 if (nethack)
387 continue;
388 score += 10 /* new_barrel_score */;
389 break;
390 case DEEP_PIT:
391 if (!nethack)
392 continue;
393 break;
394 default:
395 continue;
396 }
397
398 /*
399 * (nx,ny) must either be a space, or a square
400 * which we can convert into a space.
401 */
402 switch (grid[ny*w+nx]) {
403 case SPACE: case TARGET:
404 break;
405 case INITIAL:
406 score += 3 /* new_space_score */;
407 break;
408 default:
409 continue;
410 }
411
412 /*
413 * (npx,npy) must also either be a space, or a
414 * square which we can convert into a space.
415 */
416 switch (grid[npy*w+npx]) {
417 case SPACE: case TARGET:
418 break;
419 case INITIAL:
420 score += 3 /* new_space_score */;
421 break;
422 default:
423 continue;
424 }
425
426 /*
427 * That's sufficient to tag this as a possible
428 * pull right now. We still don't know if we
429 * can reach the required player position, but
430 * that's a job for the subsequent BFS phase to
431 * tell us.
432 */
433 pulls[npulls].ox = x;
434 pulls[npulls].oy = y;
435 pulls[npulls].nx = nx;
436 pulls[npulls].ny = ny;
437 pulls[npulls].score = score;
438#ifdef GENERATION_DIAGNOSTICS
439 printf("found potential pull: (%d,%d)-(%d,%d) cost %d\n",
440 pulls[npulls].ox, pulls[npulls].oy,
441 pulls[npulls].nx, pulls[npulls].ny,
442 pulls[npulls].score);
443#endif
444 npulls++;
445 }
446#ifdef GENERATION_DIAGNOSTICS
447 printf("found %d potential pulls\n", npulls);
448#endif
449
450 /*
451 * If there are no pulls available at all, we give up.
452 *
453 * (FIXME: or perhaps backtrack?)
454 */
455 if (npulls == 0)
456 break;
457
458 /*
459 * Now we do a BFS from our current position, to find all
460 * the squares we can get the player into.
461 *
462 * This BFS is unusually tricky. We want to give a positive
463 * distance only to squares which we have to carve through
464 * INITIALs to get to, which means we can't just stick
465 * every square we reach on the end of our to-do list.
466 * Instead, we must maintain our list as a proper priority
467 * queue.
468 */
469 for (i = 0; i < w*h; i++)
470 dist[i] = prev[i] = -1;
471
472 heap[0] = py*w+px;
473 heapsize = 1;
474 dist[py*w+px] = 0;
475
476#define PARENT(n) ( ((n)-1)/2 )
477#define LCHILD(n) ( 2*(n)+1 )
478#define RCHILD(n) ( 2*(n)+2 )
479#define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0)
480
481 while (heapsize > 0) {
482 /*
483 * Pull the smallest element off the heap: it's at
484 * position 0. Move the arbitrary element from the very
485 * end of the heap into position 0.
486 */
487 y = heap[0] / w;
488 x = heap[0] % w;
489
490 heapsize--;
491 heap[0] = heap[heapsize];
492
493 /*
494 * Now repeatedly move that arbitrary element down the
495 * heap by swapping it with the more suitable of its
496 * children.
497 */
498 i = 0;
499 while (1) {
500 int lc, rc;
501
502 lc = LCHILD(i);
503 rc = RCHILD(i);
504
505 if (lc >= heapsize)
506 break; /* we've hit bottom */
507
508 if (rc >= heapsize) {
509 /*
510 * Special case: there is only one child to
511 * check.
512 */
513 if (dist[heap[i]] > dist[heap[lc]])
514 SWAP(heap[i], heap[lc]);
515
516 /* _Now_ we've hit bottom. */
517 break;
518 } else {
519 /*
520 * The common case: there are two children and
521 * we must check them both.
522 */
523 if (dist[heap[i]] > dist[heap[lc]] ||
524 dist[heap[i]] > dist[heap[rc]]) {
525 /*
526 * Pick the more appropriate child to swap with
527 * (i.e. the one which would want to be the
528 * parent if one were above the other - as one
529 * is about to be).
530 */
531 if (dist[heap[lc]] > dist[heap[rc]]) {
532 SWAP(heap[i], heap[rc]);
533 i = rc;
534 } else {
535 SWAP(heap[i], heap[lc]);
536 i = lc;
537 }
538 } else {
539 /* This element is in the right place; we're done. */
540 break;
541 }
542 }
543 }
544
545 /*
546 * OK, that's given us (x,y) for this phase of the
547 * search. Now try all directions from here.
548 */
549
550 for (d = 0; d < 4; d++) {
551 int dx = DX(d);
552 int dy = DY(d);
553 int nx = x + dx, ny = y + dy;
554 if (nx < 0 || nx >= w || ny < 0 || ny >= h)
555 continue;
556 if (grid[ny*w+nx] != SPACE && grid[ny*w+nx] != TARGET &&
557 grid[ny*w+nx] != INITIAL)
558 continue;
559 if (dist[ny*w+nx] == -1) {
560 dist[ny*w+nx] = dist[y*w+x] + (grid[ny*w+nx] == INITIAL);
561 prev[ny*w+nx] = y*w+x;
562
563 /*
564 * Now insert ny*w+nx at the end of the heap,
565 * and move it down to its appropriate resting
566 * place.
567 */
568 i = heapsize;
569 heap[heapsize++] = ny*w+nx;
570
571 /*
572 * Swap element n with its parent repeatedly to
573 * preserve the heap property.
574 */
575
576 while (i > 0) {
577 int p = PARENT(i);
578
579 if (dist[heap[p]] > dist[heap[i]]) {
580 SWAP(heap[p], heap[i]);
581 i = p;
582 } else
583 break;
584 }
585 }
586 }
587 }
588
589#undef PARENT
590#undef LCHILD
591#undef RCHILD
592#undef SWAP
593
594#ifdef GENERATION_DIAGNOSTICS
595 printf("distance map:\n");
596 for (i = 0; i < h; i++) {
597 for (j = 0; j < w; j++) {
598 int d = dist[i*w+j];
599 int c;
600 if (d < 0)
601 c = '#';
602 else if (d >= 36)
603 c = '!';
604 else if (d >= 10)
605 c = 'A' - 10 + d;
606 else
607 c = '0' + d;
608 putchar(c);
609 }
610 putchar('\n');
611 }
612#endif
613
614 /*
615 * Now we can go back through the `pulls' array, adjusting
616 * the score for each pull depending on how hard it is to
617 * reach its starting point, and also throwing out any
618 * whose starting points are genuinely unreachable even
619 * with the possibility of carving through INITIAL squares.
620 */
621 for (i = j = 0; i < npulls; i++) {
622#ifdef GENERATION_DIAGNOSTICS
623 printf("potential pull (%d,%d)-(%d,%d)",
624 pulls[i].ox, pulls[i].oy,
625 pulls[i].nx, pulls[i].ny);
626#endif
627 x = pulls[i].nx;
628 y = pulls[i].ny;
629 if (dist[y*w+x] < 0) {
630#ifdef GENERATION_DIAGNOSTICS
631 printf(" unreachable\n");
632#endif
633 continue; /* this pull isn't feasible at all */
634 } else {
635 /*
636 * Another nasty special case we have to check is
637 * whether the initial barrel location (ox,oy) is
638 * on the path used to reach the square. This can
639 * occur if that square is in state INITIAL: the
640 * pull is initially considered valid on the basis
641 * that the INITIAL can become BARRELTARGET, and
642 * it's also considered reachable on the basis that
643 * INITIAL can be turned into SPACE, but it can't
644 * be both at once.
645 *
646 * Fortunately, if (ox,oy) is on the path at all,
647 * it must be only one space from the end, so this
648 * is easy to spot and rule out.
649 */
650 if (prev[y*w+x] == pulls[i].oy*w+pulls[i].ox) {
651#ifdef GENERATION_DIAGNOSTICS
652 printf(" goes through itself\n");
653#endif
654 continue; /* this pull isn't feasible at all */
655 }
656 pulls[j] = pulls[i]; /* structure copy */
657 pulls[j].score += dist[y*w+x] * 3 /* new_space_score */;
658#ifdef GENERATION_DIAGNOSTICS
659 printf(" reachable at distance %d (cost now %d)\n",
660 dist[y*w+x], pulls[j].score);
661#endif
662 j++;
663 }
664 }
665 npulls = j;
666
667 /*
668 * Again, if there are no pulls available at all, we give
669 * up.
670 *
671 * (FIXME: or perhaps backtrack?)
672 */
673 if (npulls == 0)
674 break;
675
676 /*
677 * Now choose which pull to make. On the one hand we should
678 * prefer pulls which do less damage to the INITIAL squares
679 * (thus, ones for which we can already get into position
680 * via existing SPACEs, and for which the barrel already
681 * exists and doesn't have to be invented); on the other,
682 * we want to avoid _always_ preferring such pulls, on the
683 * grounds that that will lead to levels without very much
684 * stuff in.
685 *
686 * When creating new barrels, we prefer creations which are
687 * next to existing TARGET squares.
688 *
689 * FIXME: for the moment I'll make this very simple indeed.
690 */
691 i = random_upto(rs, npulls);
692
693 /*
694 * Actually make the pull, including carving a path to get
695 * to the site if necessary.
696 */
697 x = pulls[i].nx;
698 y = pulls[i].ny;
699 while (prev[y*w+x] >= 0) {
700 int p;
701
702 if (grid[y*w+x] == INITIAL)
703 grid[y*w+x] = SPACE;
704
705 p = prev[y*w+x];
706 y = p / w;
707 x = p % w;
708 }
709 px = 2*pulls[i].nx - pulls[i].ox;
710 py = 2*pulls[i].ny - pulls[i].oy;
711 if (grid[py*w+px] == INITIAL)
712 grid[py*w+px] = SPACE;
713 if (grid[pulls[i].ny*w+pulls[i].nx] == TARGET)
714 grid[pulls[i].ny*w+pulls[i].nx] = BARRELTARGET;
715 else
716 grid[pulls[i].ny*w+pulls[i].nx] = BARREL;
717 if (grid[pulls[i].oy*w+pulls[i].ox] == BARREL)
718 grid[pulls[i].oy*w+pulls[i].ox] = SPACE;
719 else if (grid[pulls[i].oy*w+pulls[i].ox] != DEEP_PIT)
720 grid[pulls[i].oy*w+pulls[i].ox] = TARGET;
721 }
722
723 sfree(heap);
724 sfree(prev);
725 sfree(dist);
726 sfree(pulls);
727
728 if (grid[py*w+px] == TARGET)
729 grid[py*w+px] = PLAYERTARGET;
730 else
731 grid[py*w+px] = PLAYER;
732}
733
734static char *new_game_desc(const game_params *params, random_state *rs,
735 char **aux, bool interactive)
736{
737 int w = params->w, h = params->h;
738 char *desc;
739 int desclen, descpos, descsize, prev, count;
740 unsigned char *grid;
741 int i, j;
742
743 /*
744 * FIXME: perhaps some more interesting means of choosing how
745 * many moves to try?
746 */
747 grid = snewn(w*h, unsigned char);
748 sokoban_generate(w, h, grid, w*h, false, rs);
749
750 desclen = descpos = descsize = 0;
751 desc = NULL;
752 prev = -1;
753 count = 0;
754 for (i = 0; i < w*h; i++) {
755 if (descsize < desclen + 40) {
756 descsize = desclen + 100;
757 desc = sresize(desc, descsize, char);
758 desc[desclen] = '\0';
759 }
760 switch (grid[i]) {
761 case INITIAL:
762 j = 'w'; /* FIXME: make some of these 's'? */
763 break;
764 case SPACE:
765 j = 's';
766 break;
767 case WALL:
768 j = 'w';
769 break;
770 case TARGET:
771 j = 't';
772 break;
773 case BARREL:
774 j = 'b';
775 break;
776 case BARRELTARGET:
777 j = 'f';
778 break;
779 case DEEP_PIT:
780 j = 'd';
781 break;
782 case PLAYER:
783 j = 'u';
784 break;
785 case PLAYERTARGET:
786 j = 'v';
787 break;
788 default:
789 j = '?';
790 break;
791 }
792 assert(j != '?');
793 if (j != prev) {
794 desc[desclen++] = j;
795 descpos = desclen;
796 prev = j;
797 count = 1;
798 } else {
799 count++;
800 desclen = descpos + sprintf(desc+descpos, "%d", count);
801 }
802 }
803
804 sfree(grid);
805
806 return desc;
807}
808
809static const char *validate_desc(const game_params *params, const char *desc)
810{
811 int w = params->w, h = params->h;
812 int area = 0;
813 int nplayers = 0;
814
815 while (*desc) {
816 int c = *desc++;
817 int n = 1;
818 if (*desc && isdigit((unsigned char)*desc)) {
819 n = atoi(desc);
820 while (*desc && isdigit((unsigned char)*desc)) desc++;
821 }
822
823 area += n;
824
825 if (c == PLAYER || c == PLAYERTARGET)
826 nplayers += n;
827 else if (c == INITIAL || c == SPACE || c == WALL || c == TARGET ||
828 c == PIT || c == DEEP_PIT || IS_BARREL(c))
829 /* ok */;
830 else
831 return "Invalid character in game description";
832 }
833
834 if (area > w*h)
835 return "Too much data in game description";
836 if (area < w*h)
837 return "Too little data in game description";
838 if (nplayers < 1)
839 return "No starting player position specified";
840 if (nplayers > 1)
841 return "More than one starting player position specified";
842
843 return NULL;
844}
845
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, const char **error)
907{
908 return NULL;
909}
910
911static bool 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 void game_changed_state(game_ui *ui, const game_state *oldstate,
931 const game_state *newstate)
932{
933}
934
935struct game_drawstate {
936 game_params p;
937 int tilesize;
938 bool started;
939 unsigned short *grid;
940};
941
942#define PREFERRED_TILESIZE 32
943#define TILESIZE (ds->tilesize)
944#define BORDER (TILESIZE)
945#define HIGHLIGHT_WIDTH (TILESIZE / 10)
946#define COORD(x) ( (x) * TILESIZE + BORDER )
947#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
948
949/*
950 * I'm going to need to do most of the move-type analysis in both
951 * interpret_move and execute_move, so I'll abstract it out into a
952 * subfunction. move_type() returns -1 for an illegal move, 0 for a
953 * movement, and 1 for a push.
954 */
955static int move_type(const game_state *state, int dx, int dy)
956{
957 int w = state->p.w, h = state->p.h;
958 int px = state->px, py = state->py;
959 int nx, ny, nbx, nby;
960
961 assert(dx >= -1 && dx <= +1);
962 assert(dy >= -1 && dy <= +1);
963 assert(dx || dy);
964
965 nx = px + dx;
966 ny = py + dy;
967
968 /*
969 * Disallow any move that goes off the grid.
970 */
971 if (nx < 0 || nx >= w || ny < 0 || ny >= h)
972 return -1;
973
974 /*
975 * Examine the target square of the move to see whether it's a
976 * space, a barrel, or a wall.
977 */
978
979 if (state->grid[ny*w+nx] == WALL ||
980 state->grid[ny*w+nx] == PIT ||
981 state->grid[ny*w+nx] == DEEP_PIT)
982 return -1; /* this one's easy; just disallow it */
983
984 if (IS_BARREL(state->grid[ny*w+nx])) {
985 /*
986 * This is a push move. For a start, that means it must not
987 * be diagonal.
988 */
989 if (dy && dx)
990 return -1;
991
992 /*
993 * Now find the location of the third square involved in
994 * the push, and stop if it's off the edge.
995 */
996 nbx = nx + dx;
997 nby = ny + dy;
998 if (nbx < 0 || nbx >= w || nby < 0 || nby >= h)
999 return -1;
1000
1001 /*
1002 * That third square must be able to accept a barrel.
1003 */
1004 if (state->grid[nby*w+nbx] == SPACE ||
1005 state->grid[nby*w+nbx] == TARGET ||
1006 state->grid[nby*w+nbx] == PIT ||
1007 state->grid[nby*w+nbx] == DEEP_PIT) {
1008 /*
1009 * The push is valid.
1010 */
1011 return 1;
1012 } else {
1013 return -1;
1014 }
1015 } else {
1016 /*
1017 * This is just an ordinary move. We've already checked the
1018 * target square, so the only thing left to check is that a
1019 * diagonal move has a space on one side to have notionally
1020 * gone through.
1021 */
1022 if (dx && dy &&
1023 state->grid[(py+dy)*w+px] != SPACE &&
1024 state->grid[(py+dy)*w+px] != TARGET &&
1025 state->grid[py*w+(px+dx)] != SPACE &&
1026 state->grid[py*w+(px+dx)] != TARGET)
1027 return -1;
1028
1029 /*
1030 * Otherwise, the move is valid.
1031 */
1032 return 0;
1033 }
1034}
1035
1036static char *interpret_move(const game_state *state, game_ui *ui,
1037 const game_drawstate *ds,
1038 int x, int y, int button)
1039{
1040 int dx=0, dy=0;
1041 char *move;
1042
1043 /*
1044 * Diagonal movement is supported as it is in NetHack: it's
1045 * for movement only (never pushing), and one of the two
1046 * squares adjacent to both the source and destination
1047 * squares must be free to move through. In other words, it
1048 * is only a shorthand for two orthogonal moves and cannot
1049 * change the nature of the actual puzzle game.
1050 */
1051 if (button == CURSOR_UP || button == (MOD_NUM_KEYPAD | '8'))
1052 dx = 0, dy = -1;
1053 else if (button == CURSOR_DOWN || button == (MOD_NUM_KEYPAD | '2'))
1054 dx = 0, dy = +1;
1055 else if (button == CURSOR_LEFT || button == (MOD_NUM_KEYPAD | '4'))
1056 dx = -1, dy = 0;
1057 else if (button == CURSOR_RIGHT || button == (MOD_NUM_KEYPAD | '6'))
1058 dx = +1, dy = 0;
1059 else if (button == (MOD_NUM_KEYPAD | '7'))
1060 dx = -1, dy = -1;
1061 else if (button == (MOD_NUM_KEYPAD | '9'))
1062 dx = +1, dy = -1;
1063 else if (button == (MOD_NUM_KEYPAD | '1'))
1064 dx = -1, dy = +1;
1065 else if (button == (MOD_NUM_KEYPAD | '3'))
1066 dx = +1, dy = +1;
1067 else if (button == LEFT_BUTTON)
1068 {
1069 if(x < COORD(state->px))
1070 dx = -1;
1071 else if (x > COORD(state->px + 1))
1072 dx = 1;
1073 if(y < COORD(state->py))
1074 dy = -1;
1075 else if (y > COORD(state->py + 1))
1076 dy = 1;
1077 }
1078 else
1079 return NULL;
1080
1081 if((dx == 0) && (dy == 0))
1082 return(NULL);
1083
1084 if (move_type(state, dx, dy) < 0)
1085 return NULL;
1086
1087 move = snewn(2, char);
1088 move[1] = '\0';
1089 move[0] = '5' - 3*dy + dx;
1090 return move;
1091}
1092
1093static game_state *execute_move(const game_state *state, const char *move)
1094{
1095 int w = state->p.w, h = state->p.h;
1096 int px = state->px, py = state->py;
1097 int dx, dy, nx, ny, nbx, nby, type, m, i;
1098 bool freebarrels, freetargets;
1099 game_state *ret;
1100
1101 if (*move < '1' || *move == '5' || *move > '9' || move[1])
1102 return NULL; /* invalid move string */
1103
1104 m = *move - '0';
1105 dx = (m + 2) % 3 - 1;
1106 dy = 2 - (m + 2) / 3;
1107 type = move_type(state, dx, dy);
1108 if (type < 0)
1109 return NULL;
1110
1111 ret = dup_game(state);
1112
1113 nx = px + dx;
1114 ny = py + dy;
1115 nbx = nx + dx;
1116 nby = ny + dy;
1117
1118 if (type) {
1119 int b;
1120
1121 /*
1122 * Push.
1123 */
1124 b = ret->grid[ny*w+nx];
1125 if (IS_ON_TARGET(b)) {
1126 ret->grid[ny*w+nx] = TARGET;
1127 b = DETARGETISE(b);
1128 } else
1129 ret->grid[ny*w+nx] = SPACE;
1130
1131 if (ret->grid[nby*w+nbx] == PIT)
1132 ret->grid[nby*w+nbx] = SPACE;
1133 else if (ret->grid[nby*w+nbx] == DEEP_PIT)
1134 /* do nothing - the pit eats the barrel and remains there */;
1135 else if (ret->grid[nby*w+nbx] == TARGET)
1136 ret->grid[nby*w+nbx] = TARGETISE(b);
1137 else
1138 ret->grid[nby*w+nbx] = b;
1139 }
1140
1141 ret->px = nx;
1142 ret->py = ny;
1143
1144 /*
1145 * Check for completion. This is surprisingly complicated,
1146 * given the presence of pits and deep pits, and also the fact
1147 * that some Sokoban levels with pits have fewer pits than
1148 * barrels (due to providing spares, e.g. NetHack's). I think
1149 * the completion condition in fact must be that the game
1150 * cannot become any _more_ complete. That is, _either_ there
1151 * are no remaining barrels not on targets, _or_ there is a
1152 * good reason why any such barrels cannot be placed. The only
1153 * available good reason is that there are no remaining pits,
1154 * no free target squares, and no deep pits at all.
1155 */
1156 if (!ret->completed) {
1157 freebarrels = false;
1158 freetargets = false;
1159 for (i = 0; i < w*h; i++) {
1160 int v = ret->grid[i];
1161
1162 if (IS_BARREL(v) && !IS_ON_TARGET(v))
1163 freebarrels = true;
1164 if (v == DEEP_PIT || v == PIT ||
1165 (!IS_BARREL(v) && IS_ON_TARGET(v)))
1166 freetargets = true;
1167 }
1168
1169 if (!freebarrels || !freetargets)
1170 ret->completed = true;
1171 }
1172
1173 return ret;
1174}
1175
1176/* ----------------------------------------------------------------------
1177 * Drawing routines.
1178 */
1179
1180static void game_compute_size(const game_params *params, int tilesize,
1181 const game_ui *ui, int *x, int *y)
1182{
1183 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1184 struct { int tilesize; } ads, *ds = &ads;
1185 ads.tilesize = tilesize;
1186
1187 *x = 2 * BORDER + 1 + params->w * TILESIZE;
1188 *y = 2 * BORDER + 1 + params->h * TILESIZE;
1189}
1190
1191static void game_set_size(drawing *dr, game_drawstate *ds,
1192 const game_params *params, int tilesize)
1193{
1194 ds->tilesize = tilesize;
1195}
1196
1197static float *game_colours(frontend *fe, int *ncolours)
1198{
1199 float *ret = snewn(3 * NCOLOURS, float);
1200 int i;
1201
1202 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1203
1204 ret[COL_OUTLINE * 3 + 0] = 0.0F;
1205 ret[COL_OUTLINE * 3 + 1] = 0.0F;
1206 ret[COL_OUTLINE * 3 + 2] = 0.0F;
1207
1208 ret[COL_PLAYER * 3 + 0] = 0.0F;
1209 ret[COL_PLAYER * 3 + 1] = 1.0F;
1210 ret[COL_PLAYER * 3 + 2] = 0.0F;
1211
1212 ret[COL_BARREL * 3 + 0] = 0.6F;
1213 ret[COL_BARREL * 3 + 1] = 0.3F;
1214 ret[COL_BARREL * 3 + 2] = 0.0F;
1215
1216 ret[COL_TARGET * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0];
1217 ret[COL_TARGET * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1];
1218 ret[COL_TARGET * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2];
1219
1220 ret[COL_PIT * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0] / 2;
1221 ret[COL_PIT * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1] / 2;
1222 ret[COL_PIT * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2] / 2;
1223
1224 ret[COL_DEEP_PIT * 3 + 0] = 0.0F;
1225 ret[COL_DEEP_PIT * 3 + 1] = 0.0F;
1226 ret[COL_DEEP_PIT * 3 + 2] = 0.0F;
1227
1228 ret[COL_TEXT * 3 + 0] = 1.0F;
1229 ret[COL_TEXT * 3 + 1] = 1.0F;
1230 ret[COL_TEXT * 3 + 2] = 1.0F;
1231
1232 ret[COL_GRID * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0];
1233 ret[COL_GRID * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1];
1234 ret[COL_GRID * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2];
1235
1236 ret[COL_OUTLINE * 3 + 0] = 0.0F;
1237 ret[COL_OUTLINE * 3 + 1] = 0.0F;
1238 ret[COL_OUTLINE * 3 + 2] = 0.0F;
1239
1240 for (i = 0; i < 3; i++) {
1241 ret[COL_WALL * 3 + i] = (3 * ret[COL_BACKGROUND * 3 + i] +
1242 1 * ret[COL_HIGHLIGHT * 3 + i]) / 4;
1243 }
1244
1245 *ncolours = NCOLOURS;
1246 return ret;
1247}
1248
1249static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1250{
1251 int w = state->p.w, h = state->p.h;
1252 struct game_drawstate *ds = snew(struct game_drawstate);
1253 int i;
1254
1255 ds->tilesize = 0;
1256 ds->p = state->p; /* structure copy */
1257 ds->grid = snewn(w*h, unsigned short);
1258 for (i = 0; i < w*h; i++)
1259 ds->grid[i] = INVALID;
1260 ds->started = false;
1261
1262 return ds;
1263}
1264
1265static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1266{
1267 sfree(ds->grid);
1268 sfree(ds);
1269}
1270
1271static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, int v)
1272{
1273 int tx = COORD(x), ty = COORD(y);
1274 int bg = (v & 0x100 ? COL_HIGHLIGHT : COL_BACKGROUND);
1275
1276 v &= 0xFF;
1277
1278 clip(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1);
1279 draw_rect(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1, bg);
1280
1281 if (v == WALL) {
1282 int coords[6];
1283
1284 coords[0] = tx + TILESIZE;
1285 coords[1] = ty + TILESIZE;
1286 coords[2] = tx + TILESIZE;
1287 coords[3] = ty + 1;
1288 coords[4] = tx + 1;
1289 coords[5] = ty + TILESIZE;
1290 draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT);
1291
1292 coords[0] = tx + 1;
1293 coords[1] = ty + 1;
1294 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
1295
1296 draw_rect(dr, tx + 1 + HIGHLIGHT_WIDTH, ty + 1 + HIGHLIGHT_WIDTH,
1297 TILESIZE - 2*HIGHLIGHT_WIDTH,
1298 TILESIZE - 2*HIGHLIGHT_WIDTH, COL_WALL);
1299 } else if (v == PIT) {
1300 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1301 TILESIZE*3/7, COL_PIT, COL_OUTLINE);
1302 } else if (v == DEEP_PIT) {
1303 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1304 TILESIZE*3/7, COL_DEEP_PIT, COL_OUTLINE);
1305 } else {
1306 if (IS_ON_TARGET(v)) {
1307 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1308 TILESIZE*3/7, COL_TARGET, COL_OUTLINE);
1309 }
1310 if (IS_PLAYER(v)) {
1311 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1312 TILESIZE/3, COL_PLAYER, COL_OUTLINE);
1313 } else if (IS_BARREL(v)) {
1314 char str[2];
1315
1316 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1317 TILESIZE/3, COL_BARREL, COL_OUTLINE);
1318 str[1] = '\0';
1319 str[0] = BARREL_LABEL(v);
1320 if (str[0]) {
1321 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1322 FONT_VARIABLE, TILESIZE/2,
1323 ALIGN_VCENTRE | ALIGN_HCENTRE, COL_TEXT, str);
1324 }
1325 }
1326 }
1327
1328 unclip(dr);
1329 draw_update(dr, tx, ty, TILESIZE, TILESIZE);
1330}
1331
1332static void game_redraw(drawing *dr, game_drawstate *ds,
1333 const game_state *oldstate, const game_state *state,
1334 int dir, const game_ui *ui,
1335 float animtime, float flashtime)
1336{
1337 int w = state->p.w, h = state->p.h /*, wh = w*h */;
1338 int x, y;
1339 int flashtype;
1340
1341 if (flashtime &&
1342 !((int)(flashtime * 3 / FLASH_LENGTH) % 2))
1343 flashtype = 0x100;
1344 else
1345 flashtype = 0;
1346
1347 /*
1348 * Initialise a fresh drawstate.
1349 */
1350 if (!ds->started) {
1351 /*
1352 * Draw the grid lines.
1353 */
1354 for (y = 0; y <= h; y++)
1355 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y),
1356 COL_LOWLIGHT);
1357 for (x = 0; x <= w; x++)
1358 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h),
1359 COL_LOWLIGHT);
1360
1361 ds->started = true;
1362 }
1363
1364 /*
1365 * Draw the grid contents.
1366 */
1367 for (y = 0; y < h; y++)
1368 for (x = 0; x < w; x++) {
1369 int v = state->grid[y*w+x];
1370 if (y == state->py && x == state->px) {
1371 if (v == TARGET)
1372 v = PLAYERTARGET;
1373 else {
1374 assert(v == SPACE);
1375 v = PLAYER;
1376 }
1377 }
1378
1379 v |= flashtype;
1380
1381 if (ds->grid[y*w+x] != v) {
1382 draw_tile(dr, ds, x, y, v);
1383 ds->grid[y*w+x] = v;
1384 }
1385 }
1386
1387}
1388
1389static float game_anim_length(const game_state *oldstate,
1390 const game_state *newstate, int dir, game_ui *ui)
1391{
1392 return 0.0F;
1393}
1394
1395static float game_flash_length(const game_state *oldstate,
1396 const game_state *newstate, int dir, game_ui *ui)
1397{
1398 if (!oldstate->completed && newstate->completed)
1399 return FLASH_LENGTH;
1400 else
1401 return 0.0F;
1402}
1403
1404static void game_get_cursor_location(const game_ui *ui,
1405 const game_drawstate *ds,
1406 const game_state *state,
1407 const game_params *params,
1408 int *x, int *y, int *w, int *h)
1409{
1410}
1411
1412static int game_status(const game_state *state)
1413{
1414 return state->completed ? +1 : 0;
1415}
1416
1417static bool game_timing_state(const game_state *state, game_ui *ui)
1418{
1419 return true;
1420}
1421
1422static void game_print_size(const game_params *params, const game_ui *ui,
1423 float *x, float *y)
1424{
1425}
1426
1427static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
1428 int tilesize)
1429{
1430}
1431
1432#ifdef COMBINED
1433#define thegame sokoban
1434#endif
1435
1436const struct game thegame = {
1437 "Sokoban", NULL, NULL,
1438 default_params,
1439 game_fetch_preset, NULL,
1440 decode_params,
1441 encode_params,
1442 free_params,
1443 dup_params,
1444 true, game_configure, custom_params,
1445 validate_params,
1446 new_game_desc,
1447 validate_desc,
1448 new_game,
1449 dup_game,
1450 free_game,
1451 false, solve_game,
1452 false, game_can_format_as_text_now, game_text_format,
1453 NULL, NULL, /* get_prefs, set_prefs */
1454 new_ui,
1455 free_ui,
1456 NULL, /* encode_ui */
1457 NULL, /* decode_ui */
1458 NULL, /* game_request_keys */
1459 game_changed_state,
1460 NULL, /* current_key_label */
1461 interpret_move,
1462 execute_move,
1463 PREFERRED_TILESIZE, game_compute_size, game_set_size,
1464 game_colours,
1465 game_new_drawstate,
1466 game_free_drawstate,
1467 game_redraw,
1468 game_anim_length,
1469 game_flash_length,
1470 game_get_cursor_location,
1471 game_status,
1472 false, false, game_print_size, game_print,
1473 false, /* wants_statusbar */
1474 false, game_timing_state,
1475 0, /* flags */
1476};